smarx.com

developing for the new web

Why the UpdateProgress won't display

This keeps coming up on the forums, probably because it's pretty counterintuitive. What do you think will happen if I click the button in the following example?

<%@ Page Language="C#" %> 
<script runat="server"> 
    protected void sleep(object sender, EventArgs e) { System.Threading.Thread.Sleep(5000); }     
</script> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<body> 
    <form id="form1" runat="server"> 
        <asp:ScriptManager ID="ScriptManager1" runat="server" /> 
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> 
            <ContentTemplate> 
                <%= DateTime.Now %> 
                <asp:Button ID="Button1" runat="server" Text="Click Me!" OnClick="sleep" /> 
            </ContentTemplate> 
        </asp:UpdatePanel> 
        <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> 
            <ProgressTemplate> 
                Working... 
            </ProgressTemplate> 
        </asp:UpdateProgress> 
    </form> 
</body> 
</html>

Right! The UpdateProgress will show "Working..." for five seconds while the async postback completes. Okay, that was an easy one, just a warm-up. What about this code?

<%@ Page Language="C#" %> 
<script runat="server"> 
    protected void sleep(object sender, EventArgs e) { System.Threading.Thread.Sleep(5000); UpdatePanel1.Update(); } 
    protected void Page_Load(object sender, EventArgs e) { ScriptManager.GetCurrent(Page).RegisterAsyncPostBackControl(Button1); } 
</script> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<body> 
    <form id="form1" runat="server"> 
        <asp:ScriptManager ID="ScriptManager1" runat="server" /> 
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> 
            <ContentTemplate> 
                <%= DateTime.Now %> 
            </ContentTemplate> 
        </asp:UpdatePanel> 
        <asp:Button ID="Button1" runat="server" Text="Click Me!" OnClick="sleep" /> 
        <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> 
            <ProgressTemplate> 
                Working... 
            </ProgressTemplate> 
        </asp:UpdateProgress> 
    </form> 
</body> 
</html>

Here I've moved the Button outside of the UpdatePanel and instead made it do an async postback by calling RegisterAsyncPostBackControl(). Finally, I'm causing the UpdatePanel to update by calling Update() on it in the code-behind.

This time, the UpdateProgress doesn't display. If that surprises you, think for a minute about the order of what needs to happen:

  1. You click the button.
  2. On the client, the UpdateProgress decides whether or not to display itself. (For those hardcore geeks out there, UpdateProgress hooks the BeginRequest event on the PageRequestManager.)
  3. The async postback is sent back to the server.
  4. The server sends back the updates that should be applied to the page.
  5. The UpdateProgress hides itself if necessary. (This is in EndRequest.)

Now it should be clear why the UpdateProgress can't show itself in the above scenario. It simply doesn't know whether or not the UpdatePanel it's associated with is going to be updated. In this case, it's always updated, but there's no way to know that. (Maybe you check the current date and only update the panel if it's a Tuesday...)

Okay, now try this example:

<%@ Page Language="C#" %> 
<script runat="server"> 
    protected void sleep(object sender, EventArgs e) { System.Threading.Thread.Sleep(5000); }     
</script> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<body> 
    <form id="form1" runat="server"> 
        <asp:ScriptManager ID="ScriptManager1" runat="server" /> 
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional"> 
            <ContentTemplate> 
                <%= DateTime.Now %> 
            </ContentTemplate> 
            <Triggers> 
                <asp:AsyncPostBackTrigger ControlID="Button1" /> 
            </Triggers> 
        </asp:UpdatePanel> 
        <asp:Button ID="Button1" runat="server" Text="Click Me!" OnClick="sleep" /> 
        <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> 
            <ProgressTemplate> 
                Working... 
            </ProgressTemplate> 
        </asp:UpdateProgress> 
    </form> 
</body> 
</html>

Here I've used a trigger to say that when Button1 is clicked, Update1 should be updated. Are you surprised to learn that the UpdateProgress control doesn't display with the above code?

If you read the documentation carefully, you'll see that this is the documented behavior (emphasis added):

You can associate the UpdateProgress control with a single UpdatePanel control by setting the progress control's AssociatedUpdatePanelID property. In that case, the UpdateProgress control displays a message only when a postback originates inside the associated UpdatePanel control.

This is actually a very similar scenario to the previous example. Triggers happen on the server, not on the client. That means that when the UpdateProgress control has to decide whether or not to display, it doesn't yet know whether the associated UpdatePanel is going to be updated. It uses a simple heuristic that just checks to see if the control issuing the postback is inside the associated UpdatePanel or not. In this case, it's not, so the UpdateProgress doesn't show up.

Okay, one last example now that I have you warmed up:

<%@ Page Language="C#" %> 
<script runat="server"> 
    protected void sleep(object sender, EventArgs e) { System.Threading.Thread.Sleep(5000); }
</script> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<body> 
    <form id="form1" runat="server"> 
        <asp:ScriptManager ID="ScriptManager1" runat="server" /> 
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false"> 
            <ContentTemplate> 
                <%= DateTime.Now %> 
                <asp:Button ID="Button1" runat="server" Text="Click Me!" OnClick="sleep" /> 
            </ContentTemplate> 
        </asp:UpdatePanel> 
        <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1"> 
            <ProgressTemplate> 
                Working... 
            </ProgressTemplate> 
        </asp:UpdateProgress> 
    </form> 
</body> 
</html>

This is the same as the first sample except that I set ChildrenAsTriggers="false". Because of that setting, UpdatePanel1 never actually gets updated. Despite that fact, the UpdateProgress still displays, because it's using that really simple heuristic of, "Is the control inside the UpdatePanel?" The behavior you end up with is that when you click the button, "Working..." shows up for five seconds and then disappears, with no other change to the page.

There are definitely some counterintuitive scenarios here. The thing to keep in mind is that the UpdateProgress control is really not that smart, and it makes all its decisions on the client. The UpdatePanelAnimation extender in the AJAX Control Toolkit is faced with the same challenges around when to play the "OnUpdating" animation, and I believe it always behaves in the same way as the UpdateProgress control. UPDATE: Actually, the UpdatePanelAnimation "OnUpdating" animation plays on all async postbacks, so they went with the opposite default as compared to the UpdateProgress control.

What can you do about it?

I'd feel remiss if I told you all those shortcomings and didn't tell you what you can do about it. My recommendation if you need better control over when the UpdateProgress control displays is to hook the client-side BeginRequest and EndRequest events of the PageRequestManager and do whatever complicated work you want to.

For example, if you add the following script to any of the above examples, it will display the UpdateProgress control whenever you click Button1. (No need to manually hide it... the control will do that for us.)

<script type="text/javascript"> 
    Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(function (sender, args) { 
        if (args.get_postBackElement().id == '<%= Button1.ClientID %>') { 
            $find('<%= UpdateProgress1.ClientID %>').get_element().style.display = 'block'; 
        } 
    }); 
</script>
said:
Adam said:
Great post, thanks!!!!!
Yang said:
great post
David said:
Nice! thanks!
Tarak said:
Works beautifully!
Janez Drnovšek said:
I dont have time to read all this stuff.
I have lost my laptop.
Michael McGuire said:
There seems to be a problem though in the documentation (or I am misunderstanding it). You point to a tutorial example that makes the statement above, but in the documentation for the AssociatedUpdatePanelID property for the UpdateProgress control it states:

"The default value is an empty string, which means that the UpdateProgress control is not associated with a specific UpdatePanel control. Therefore, asynchronous postbacks that originate from inside any UpdatePanel control or postbacks from controls that are triggers for the panel cause the UpdateProgress control to display its ProgressTemplate content."

It seems that the second part "or postbacks from controls that are triggers for the panel" is misleading or simply wrong. If it is true, then the third example should work as expected, no?
said:
Fabien said:
After a lot of test on my page witch content several update panel.
I notice that if I just put the UpProgress attribute visisible to true without AssociatedUpdatePanelID or DisplayAfter, it works honestly.
It seems to have the same effect that the display="Block" of the javascript
Armaghan said:
Fabien is right. If you do not associate an UpdProgress with UpdPanel, both the child controls and Triggers show the UpdProgress..!! Weird!
Tim H said:
Hi,
The first example code does not work. 1) You need to have (lessthan)%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
Namespace="System.Web.UI" TagPrefix="asp" %(geater than)
added to the beginning in order to get the asp tags to be recognized.
2) You also need to add a Head section with a script reference:
(less than)script src="Scripts/MicrosoftAjax.js" type="text/javascript"></script(greater than)
to get the JavaScript stuff (and copy it from the magic install place to your web site)
However, this still does not allow the Progress text to display. It compiles clean, but doesn't show anything. What else is missing?
Thanks.
Tim H said:
Hi,
Found my problem, there were 2.
1) All the Web.Config changes for AJAX were not installed. Kind of hard to find that documented, but finally did.
2) Somehow the line
(greaterthan)xhtmlConformance mode="Legacy"/(lessthan) was inserted into the web.config file when the app was first created. Take that out and the Ajax stuff works fine.
Donny said:
You ROCK!!!
This was exactaly what I was looking for.
Olivehour said:
Yes, yes, yes! All of the above kudos are well deserved. I searched the client reference but didn't find that documentation. Thanks for posting it!
Ryan said:
been on this prob for an hour...you gave me the enlightenment...10x
said:
the ferryman said:
How do i implement "DisplayAfter" feature using the above workaround? Thanks.
said:
Skywalker said:
Doesn't work, I get the following javascript error:

"Error: 'Sys' no esta definido"

traduction:

"Error: 'Sys' is not defined" jaja..

Did I miss something? Any Idea?

Thanks
YYZBear said:
To fix "Error: 'Sys' is not defined", add "defer" to the script block. Example:

&lt;script language="javascript" defer&gt;
// add your script here...
&lt;/script&gt;

If that doesn't fix your problem, you may want to check the link below:

http://weblogs.asp.net/chrisri/archive/2007/02/02/demystifying-sys-is-undefined.aspx

Good luck
YYZBear said:
For security reason, I cannot post the problem brakets on the example below, but it would be something like this:

[script language="javascript" defer]
// add your script here
[/script]

replace the square brackets above with the proper html brakets.
said:
said:
Michael Pine said:
I have the opposite issues, I have a gridview which fires the Postback and shows the UpdateProgress ok and I assume this is because it is a child of the UpdatePanel so shows the UpdateProgress.

However, it does not disappear after the Postback is finished, I have tried making Visible=false in Codebehind, does not work and I tried to register javascript to hide the div manually and that still does not work.

I just get stuck with my Progress message on the screen.
Michael said:
Thanks for that...you're a champion!
Pankaj Sontakke said:
Thanks.. Great Article..
Seb said:
Thanks!
kajihen said:
Great! Thanks!
said:
psp game downloads said:
what's up?
Richard Carlin said:
This is a brilliant little piece of info, concisely put with maximum info.

Ta very much
shahzad said:
Nice article ...
I am loving to explore more ,,,,
Krishna said:
thank you very much
Miguel said:
Thank's, well done
ASP NET Web Services said:
I had same problem... thanks for the solution...
Txomin said:
I've got an issue, Update progress won't display from controls (outside the update panel) that are triggers for the panel,even when AssociatedUpdatePanelID is not set.

According to the documentation:

One UpdateProgress control on the page can show a progress message for all UpdatePanel controls on the page. Asynchronous postbacks originating inside an UpdatePanel control cause the UpdateProgress control to display its message. "Postbacks from controls that are triggers for the panel also display the message."

You can associate the UpdateProgress control with a single UpdatePanel control by setting the progress control's AssociatedUpdatePanelID property. In that case, the UpdateProgress control displays a message only when a postback originates inside the associated UpdatePanel control.
Txomin said:
I've got a page with an update panel (a label and a button inside it), an update progress and a button outside the panel.

When AssociatedUpdatePanelID is set or not, and I click on the outside button, the progress won't display.It displays just when the inside button is clicked
Santa is coming said:
I kept getting 'sys is undefined'. Moving the script after the scriptmanager fixed that for me.

Add your own comment

Your name:
Your website/mailto:
Your comment: