Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Dynamic UpdateProgress Control

4.21/5 (9 votes)
28 Apr 2008Public Domain4 min read 1   1.3K  
Hide sections of the page when an UpdateProgress control is activated.

Introduction

I spent the last week playing with the UpdateProgress control that comes as part of the AJAX Extensions. For the most part, the control itself is quite nice to use but, there was one thing that I found to be a real pain: I could not get the control to hide an area of a page while it was activated. Although this sounds trivial, it got to be a real pain trying to find areas in my page design that would lend well to an UpdateProgress being displayed and still be intuitive to the user what was happening.

If you have struggled with this already, you know exactly what I'm talking about. If you have not, I'm sure you will. This control will save you a lot of headache.

Using the code

The ExtendedUpdateProgress control is actually very easy to use. You implement it on a page as you would a regular UpdateProgress control, with a few added properties. First of all, always make sure you have a script manager on the page.

ASP.NET
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>

Then, implement the ExtendedUpdateProgress control on your page (this is a code example from the demo that you can download).

ASP.NET
<%@ Register Assembly="UpdateProgress" 
    Namespace="UpdateProgress.ExtendedUpdateProgress" TagPrefix="cc1" %>

<div id="test1Div">
<asp:Label ID="test1Label" runat="server" Text="Time will appear here">
</asp:Label>
</div>

<cc1:ExtendedUpdateProgress ID="ExtendedUpdateProgress1" runat="server" 
  AssociatedUpdatePanelID="test1UpdatePanel" 
  DisplayAfter="400" DivToHide="test1Div" 
  DynamicLayout="true" ImageToDisplay="Small">
</cc1:ExtendedUpdateProgress>

In order for this control to work, you must specify an AssociatedUpdatePanelID. The new properties are the DivToHide property and the ImageToDisplay property.

In this example, I wanted to dynamically create the template of the UpdateProgress control with an animated GIF based on the ImageToDisplay property. You can remove this property and the code that replaces the template from the code in the control if you do not wish to have the same behaviour. If you do this, you will need to identify the ProgressTemplate as you would with the regular UpdateProgress control. With it as it is now, the ProgressTemplate doesn't need to be defined at all.

The DivToHide property takes the Id of a div and, when the UpdateProgress control is triggered, the control will hide the associated div. When the UpdateProgress is done, the div will re-appear.

Understanding the code

The first thing the ExtendedUpdateProgress control does is render all the appropriate JavaScript to the page and register itself with its divToHide, AssociatedUpdatePanel, and its clientId. As there can be more than one UpdateProgress control on a page, we can't hard-code specific IDs and tags in the generated JavaScript. So, we need to register the valid information so we can identify it later. The SetUpdateProgressControl function is called from a RegisterStartupScript method off the Page.ClientScript.

JavaScript
var updateProgressControls
function UpdateProgress(updatePanelId, controlToHideId, updateProgressId)
{
  this.ControlToHideId = controlToHideId;
  this.UpdatePanelId = updatePanelId;
  this.UpdateProgressId = updateProgressId;
} 
function SetUpdateProgressControl(updatePanelId, 
         controlToHideId, updateProgressId)
{
  /* Intantiate the updateProgressControls array if null */
    if (updateProgressControls == null)
    {
      updateProgressControls = new Array();
    }
  /* if the control already exists replace it, else insert into the array*/
  var controlIndex = 0;
  for (var i=0, len=updateProgressControls.length; i<len; ++i )
    {
      if (updateProgressControls[i].UpdatePanelId == updatePanelId){
        controlIndex = i;
        break;}
      controlIndex = controlIndex + 1;
    }
  updateProgressControls[controlIndex] = 
    new UpdateProgress(updatePanelId, controlToHideId, updateProgressId);
}

After the appropriate info has been registered to the JavaScript, the PageRequestManager's BeginRequest and EndRequest methods are overridden.

C#
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

When a postback request is started, it takes a look at the UpdatePanel that caused the postback. If it has been registered in the ExtendedUpdateProgress control, then it hides the associated DivToHide. One special note, as there is not an event that fires when an UpdateProgress control is activated, I had to take the DisplayAfter property of the UpdateProgress and use the window.setTimeout to delay the function call. I added an extra millisecond to the timeout as the hideControl checks if the UpdateProgress is displayed before hiding the associated div, just an extra precaution.

JavaScript
function BeginRequestHandler(sender, args)
{ 
  /* If the postback is called from with 
     a registered UpdatePanel, hide it's ControlToHide */ 
  var updateControl = GetUpdateControl(sender._postBackSettings.panelID); 
  if (updateControl != null) 
  { 
    var postBackPanelControl = document.getElementById('postBackPanel'); 
    postBackPanelControl.value = sender._postBackSettings.panelID; 
    var cbk = Function.createCallback(hideControl, 
              {ControlToHideId: updateControl.ControlToHideId, 
               UpdateProgressId: updateControl.UpdateProgressId}); 
    window.setTimeout(cbk, " + (DisplayAfter + 1) + "); 
  } 
}
function hideControl(e, context)
{ 
  var controlId = (e.ControlToHideId == null)?
                   context.ControlToHideId:e.ControlToHideId; 
  var updateProgressId = (e.UpdateProgressId == null)?
                          context.UpdateProgressId:e.UpdateProgressId; 
  var divToHide = document.getElementById(controlId); 
  var progressDiv = document.getElementById(updateProgressId); 
  /* If the UpdateProgress control for this UpdatePanel 
     is visible, hide the associated div */ 
  if (progressDiv.style.display != 'none') 
  {
    divToHide.style.visibility = 'hidden'; 
    divToHide.style.display = 'none';
  } 
}

When the postback request ends, it shows the associated div.

JavaScript
function EndRequestHandler(sender, args)
{  
  /* If the postback is called from with 
     a registered UpdatePanel, show it's ControlToHide */  
  var postBackPanelControl = document.getElementById('postBackPanel');  
  var panelId = (sender._postBackSettings.panelID == null)?
                 postBackPanelControl.value:sender._postBackSettings.panelID;  
  var updateControl = GetUpdateControl(panelId);  
  if (updateControl != null) 
  { 
    showControl(updateControl.ControlToHideId);
  }  
  postBackPanelControl.value = '';  
}  
function showControl(controlId)
{  
  var divToHide = document.getElementById(controlId);  
  /* show the associated div */  
  divToHide.style.visibility = 'visible';  
  divToHide.style.display = 'block';
}

Both the begin and end requests use a function GetUpdateControl. This function does nothing more than take the panelId passed in and find the registered UpdateProgress information for that Panel.

JavaScript
function GetUpdateControl(panelId) 
{  
  if (panelId == null || panelId == '')  
    return null;  
  /* get the UpdatePanel ID */  
  var rawPanelId = panelId.split('|')[0];  
  var updatePanelId = 
      rawPanelId.substring(rawPanelId.lastIndexOf('$')+1, rawPanelId.length)  
  /* find the update panel in the updateProgressControls array and return */  
  for (var i=0, len=updateProgressControls.length; i<len; ++i )  
  { 
    if (updateProgressControls[i].UpdatePanelId == updatePanelId)  
    {
      return updateProgressControls[i];  
    }
  }
  return null;  
}

The code behind the ExtendedUpdateProgress control is fairly extensive, but it has been documented thoroughly and the implementation of the control is extremely easy.

Points of interest

I would definitely recommend taking a look at how this control works. There is extensive JavaScript that gets rendered to the page to allow this control to work and manage itself. It also needs to be able to allow multiple UpdateProgress controls on one page. Because of this, the JavaScript rendered to the page needs to be able to "register" each control appropriately.

Conclusion

Please note that this code will only work in IE as it is defined right now. I am currently looking into making it compatible for Firefox and Safari, and will post updates as I have them. Updated: April 29. Added support for Firefox and Safari. Tested with Firefox 2.0 and Safari 3.1.1. If you would like a description of the changes, please feel free to ask. Please enjoy the code, and if you have any question/comments/concerns, please do not hesitate to ask.

History

  • April 28, 2008 - Initial article.
  • April 29, 2008 - Uploaded new source code and demo files to support Firefox and Safari.
  • April 30, 2008 - More description has been asked for. Please check out the new "Understanding the code" section above.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication