Mouse pointer as ASP.NET AJAX progress indicator
AJAX, ASP.NET, CSS, UI By Dave Ward on January 1st, 2007Update: If you’re looking for something more graphical, also see my example of using a CSS style as AJAX progress indicator.
The ASP.NET AJAX UpdatePanel control provides us a quick and easy way to AJAX enable websites without changing our familiar design patterns. It’s certainly much better than using full postbacks in many situations.
However, it lacks usability. While the user waits on the async postback to occur, they are left without any of the usual indicators. We’ve spent decades training our users to wait when they see an hourglass icon. Why throw all that away for a spinning Web 2.0 progress indicator that means little to an average user?
Luckily, the ASP.NET AJAX framework provides us with tools to correct this shortcoming.
We’ll start with a run of the mill UpdatePanel setup:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <html> <body> <div id="Container"> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <asp:Label ID="Label1" runat="server" Text="Update Me" /> <br /><br /> <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /> </ContentTemplate> </asp:UpdatePanel> </form> </div> </body> </html>
public partial class _Default : System.Web.UI.Page { protected void Button1_Click(object sender, EventArgs e) { Thread.Sleep(3000); Label1.Text = DateTime.Now.ToString(); } }
This is how it works with just an UpdatePanel:
First, we need to expand the Container div to fill the entire page using CSS:
html,body { height:99% } #Container { height:99%; min-height:99%; } html>body #outer { height:auto }
Next, we need to hook and handle the initializeRequest and endRequest events exposed by the ASP.NET AJAX framework, using this JavaScript embedded in the page anywhere after the ScriptManager control:
<script language="javascript"> var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_initializeRequest(InitializeRequest); prm.add_endRequest(EndRequest); function InitializeRequest(sender, args) { $get('Container').style.cursor = 'wait'; } function EndRequest(sender, args) { $get('Container').style.cursor = 'auto'; } </script>
To further enhance usability, we can disable the update button for the duration of the async postback:
<script language="javascript"> var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_initializeRequest(InitializeRequest); prm.add_endRequest(EndRequest); function InitializeRequest(sender, args) { $get('Container').style.cursor = 'wait'; // Get a reference to the element that raised the postback, // and disables it. $get(args._postBackElement.id).disabled = true; } function EndRequest(sender, args) { $get('Container').style.cursor = 'auto'; // Get a reference to the element that raised the postback // which is completing, and enable it. $get(sender._postBackSettings.sourceElement.id).disabled = false; } </script>
Now, we have a much better user experience:
Of course, this is only the tip of the iceberg. The code could use some refinement, but it demonstrates the concept well.
Similar posts
No related posts.
What do you think? Your comments are welcome.
I appreciate all of your comments, questions, and other feedback, but please try to stay on topic. If you have a question unrelated to this post, I recommend posting on the ASP.NET forums instead.
If you're replying to an existing comment, please use the threading feature. To do this, click the "Reply to this" link underneath the comment you're replying to.


I have been looking for this, thank you so much, great article!
Me too! I’ve searched everywhere for this functionality. Thank you so much for this. :)
Sensational stuff. Thanks.
Thank you. sooo much.
Excellent article, and the animations for demonstrating what you are trying to achieve are a great tool – well done and thanks
Hi i am using 2 updatepanel in my webpage but i want to find out the updatepanel id in EndRequest(sender, args) function, so i can call different javascript as per updatepanel ID.
In that EndRequest function definition, sender._panelsToRefreshIDs will contain an array of UpdatePanel IDs to be refreshed at the end of the request. You can parse that to act accordingly.
Thanks a million!
thank alots! that’s really what i’ve been waiting for.
Hi. I try your method but I succeded to make it works only without the use of a MasterPage.
I tried to put in the masterpage the div with id=”Content” and your code but it dosn’t change the mouse icon when I use an UpdatePanel in a page that use that MasterPage ! Do you have an idea why?
What you have to keep in mind is that you can only change the mouse cursor over an HTML element, not necessarily on a wholesale basis. In the example, the effect is that it changes in the entire window, because of the full page “Container” div.
In a master/content page, depending on the structure of your page, that “Container” div inserted in a content area would probably be constrained to the size of the content area. So, you wouldn’t be able to get the effect in the full screen area.
However, if your master page template has a container type element at the top level, you can change the $get(‘Container’) calls to manipulate that instead.
Master:
Example master
I cannot post aspx code example, sorry!!!
I tried this one. But the hour glass is not coming for the mouse icon. Any clues.
See my answer to the comment above yours.
bless you! I’ve been looking all over for this!
I wanted the whole page to be in wait mode, so I used this instead of just affecting the container:
document.body.style.cursor = “wait”;
and
document.body.style.cursor = “default”;
The body solution worked better for me.
Note, a good standard to use is a css global reset like this:
* { padding: 0; margin: 0; }You can now have the body fill the screen with the following additional css:
body { width: 100%; height: 100%; }The above replaces the css supplied by Dave and reduces the implemtation. Also, you dont need the container div. Make sure you use the change to the javascript by “Anonymous”
This worked with master pages too.
I discovered this when on a test page i did a:
body { style="border: 1px solid black;" }… and found the body was only the height of my last element on the page.
thank you for a great solution Dave. I subscribe to your rss feed.
(hope the css i supplied posts)
Excellent article… worth reading it…It helped me get thro’ the issue that I was looking for a very long time..
cheers
Thanks!!!
Hi,
article is really great .but in my case i am using
asp.net 2003 with Ajax and i think it wont support
“var prm = Sys.WebForms.PageRequestManager.getInstance”
means there is no class with Sys.
any clues???
You may be running into this problem: http://encosia.com/2007/08/16/updated-your-webconfig-but-sys-is-still-undefined/
first of all thnks for your quick reply , yes i think the provided link is helpful for me but i dont know how to use?
please focus !!!!
Great article. Just what I was looking for.
Your code worked like a charm except for the disable button javascript which errored for me.
But my problem is I am using an link not a button.
And when the cursor is over the link, it shows the hand regardless, only when you remove it from the hovering above the link , do you see the hourglass.
Any ideas on how to solve that too?
Dave,
Please see below small correction in EndRequest call. I was getting javascript exception related to index without if condition.
if (sender._postBackSettings.sourceElement.id.length > 0) {
$get(sender._postBackSettings.sourceElement.id).disabled = false;
}
I add a class on the body tag
body.loading * { cursor: wait !important }
and then remove it when loading is done, but in IE and Chrome it does not change back until i move the mouse.
Anyone experiencing this problem, and maybe found a solution ?
Finally! Somebody addresses this obviously too-long missing behavior. Kudos.
What follows is not a slam against your article, rather a trip into the “obvious”…
For decades, in tools like Delphi, and for desktop applications, the developer had to do only this:
Screen.Cursor := crHourglass;
// Potentially long-running code here
Screen.Cursor := crDefault:
I would suggest that our web development tools, frameworks, whatever, should make it equally easy on the developer. This behavior is virtually an unstated requirement for all desktop apps, so why should web development make it any harder to achieve?
Yes, I understand there are complexities beyond desktop applications. I simply think those should be overcome in the implementation of the frameworks and languages we use for web development.
Great stuff! This idea is so simple I can’t believe I’ve never thought of this or seen it before! I really like that it flows with a convention people already understand.
I’ve just found your blog and I’ve gotta say every post I’ve read is excellent. Well written, interesting, and unique. Subscribed!