Download the source here.
Introduction
Providing the user feedback to their actions is the most important aspect in creating a responsive application. No user ever wants to click on a button or link and not know if anything actually happened. This is especially common when AJAX is in use. The user no longer has the familiar page flash to indicate that "something" happened. With AJAX only the portion of the screen that needs changed will be updated. If no progress indicator is used then the user is left to figure out themselves if the action actually started and if it's even finished yet.
Enter the Update Progress control for asp.net AJAX 1.0. This control is designed to give some visual feedback to the user when an asyn-postback is occuring. This does help the situation a bit, but it has problems and limitation. The main issue I have with a status indicator like this is that it will easily get lost on the page. The user may or may not even see the little spinning .gif that you have set to show within the control.
If no progress indicator is used then the user is left to figure out themselves if the action actually started and if it's even finished yet.
A Better Way
There needs to be a more clearly visible indication that something is going on. We must leave no doubt to the user that the following have taken place:
- The click started the action and I can no longer click on anything else
- The action is still being completed and I still can't click on anything else
- The action is finished and I can now click on something else
There are many ways to make the above clear to the user, but I think the best way is to show the status by the user's cursor. There's little chance the user will not see the status indicator because it is attached to their cursor which is normally hovering over or close to what the user is looking at on the page. I want to accomplish this with the following requirements:
- No use of the built-in UpdateProgress control
- Show the status for all async postbacks by the user's cursor
- Disable the page while the postback takes place
- Work on all major browsers
- Do not slow down the page rendering
- Work with or without an UpdatePanel
- Use Asp.net AJAX 1.0
Let's do it
First we'll set up the aspx page. Here's the code:
Page Load: <%= DateTime.Now.ToString() %>
<br />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
Async-postback time:
<asp:Label ID="StatusIndicator" runat="server" />
<br />
<asp:Button ID="doProcess" runat="server"
Text="Start" OnClick="doProcess_Click" />
</ContentTemplate>
</asp:UpdatePanel>
Very simple stuff. Just an update panel with a button that when clicked gets the time on the server and assigns it to a label control. The original page load time is displayed at the top to compare to the async-postback time.
Next let's look at the JavaScript. I'm using the standard way to hook into the Asp.net AJAX PageRequestManager:
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(InitializeRequest);
prm.add_endRequest(EndRequest);
Here is the most important part of the code. This where the request is initialized and all the action starts. On every async-postback is made this is called. You can do all kinds of fun stuff here:
/hook into InitializeRequest
function InitializeRequest(sender, args)
{
//this will cancel a request if another one is already running
if (prm.get_isInAsyncPostBack()) {
args.set_cancel(true);
}
else
{
//show the disable mask on the page
$get('ProgressMask').style.display = "block";
//set initial animation text
Animate();
//Start animation using setInterval while request is processed
intervalID = window.setInterval("Animate()", 1000);
}
}
There is a DIV on the aspx page called "ProgressMask". This is how I am disabling the page when a request is made. The DIV covers the entire screen during the request with a higher z-index than anything else which prevents anything else from being clicked. There's also some code in there that prevents another request from coming in if one is already running.
This is the bit of code that runs when the request finally ends. All I do here is stop the animation, set the styles back to normal and assign the "finished" text.
//hook into the EndRequest event
function EndRequest(sender, args)
{
window.clearInterval(intervalID); //process is done so stop animating
$get("ProgressAnimate").className = "ProcessComplete";
$get("ProgressAnimate").innerHTML = "Complete"; //set the text of what you want to display when finished
intervalID = window.setInterval("ClearAnimate()", 3000) //Hide after 3 seconds
$get('ProgressMask').style.display = ""; //hide the disable mask on the page
window.clearInterval(intervalID);
}
function Animate()
{
$get("ProgressAnimate").className = "ActiveProcess";
$get("ProgressAnimate").innerHTML = GetStatusText($get("ProgressAnimate").innerHTML); //grabs the text while processing
}
function ClearAnimate()
{
$get("ProgressAnimate").className = "AnimateNormal"; //set CSS back to normal
}
function GetStatusText(ctrText)
{
//set the animation text
return (ctrText=="Please wait.")?"Saving...":"Please wait.";
}
I have posted the source code at the top of the article. If you have any questions or comments please feel free to leave one below. Thanks!
