Click here to Skip to main content
15,867,308 members
Articles / Web Development / ASP.NET
Article

Dynamic UpdateProgress Control

Rate me:
Please Sign up or sign in to vote.
4.21/5 (9 votes)
28 Apr 2008Public Domain4 min read 64.9K   1.3K   45   6
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


Written By
President
Canada Canada
Jamie is an experienced IT professional, Scrum Master, Agile practitioner and Entrepreneur. He has experience leading teams of various sizes, in companies of various sizes to nothing but success. He's helped people from ideas on a napkin to full business plans become successful and profitable businesses. His company, located in the area of Calgary, Chunk Munk Technologies, is arguably one of the industry leaders with respect to customer service and satisfaction. Chunk Munk operates in what Jamie calls “ethics based business”.

Comments and Discussions

 
Generalmuch simpler way Pin
jek_com16-Mar-10 6:15
jek_com16-Mar-10 6:15 
GeneralGreat control - minor tweak Pin
fursen18-Dec-09 6:12
fursen18-Dec-09 6:12 
GeneralNeat code Pin
Armand29-May-09 8:04
Armand29-May-09 8:04 
Generalsome suggestions/doubts Pin
tvj0828-Jan-09 9:08
tvj0828-Jan-09 9:08 
Hi, I liked your control, very interesting. I have some doubts as well as some suggestions. I appreciate your response.

1) In 'RegisterMainFunctions' you are using 'ScriptManager.RegisterStartupScript(Page,'
which means script is registered to page every time asynchronous call occurs, even for update panels which do not have your extended control inside them. Isn't it a good idea to use 'ScriptManager.RegisterStartupScript(Control,..', so script is registered only for those update panels inside which Control exists.
2) pageLoading event can be used instead of beginRequest. MS recommends not to use private variables such as 'sender._postBackSettings.panelID'. pageLoading will giving you access to panelsUpdating.
3) In 'InitializeControl' function, use client ids of AssociatedUpdatePanelID and DivToHide controls. Otherwise, your control will not work correctly inside user controls. You know how asp.net prefixes all control ids inside user control.
4) Always use 'typeof' instead of 'GetType()', especially when you are developing a control. http://blogs.ipona.com/james/archive/2006/10/03/6710.aspx[^]

Sorry for picking on your code. I have lot of time on my hands, it seems. Smile | :) I should use that time to write some controls myself, instead of picking on other's code.
GeneralProblem with safari Pin
ender_9092-Sep-08 20:46
ender_9092-Sep-08 20:46 
Generalmore description Pin
Abhijit Jana30-Apr-08 1:47
professionalAbhijit Jana30-Apr-08 1:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.