UpdatePanel - In Depth

For all the ASP.Net AJAX (formerly "Atlas") lovers out there, I decided to put together some information (I consider useful) in this blog - this is also for folks who might have missed my webcast on the topic which was delivered on 10/26.

The Partial Page Rendering feature has been one of the most popular feature in the ASP.Net AJAX framework. The feature is built using 3 components - the ScriptManager and the UpdatePanel on the server and the PageRequestManager object on the client.

 The ScriptManager controls the partial rendering behavior by the 'EnablePartialRendering' property, when set to true the page is enabled for async post-backs and any UpdatePanel controls on the page can be updated as a result of this post-back. It also provides for global properties such as AsyncPostBackTimeout and AllowCustomErrorsRedirect which govern the handling of the page in certain common scenarios. The AllowPostBackTimeout causes a timeout error to return to the client if the async post-back took longer than the timeout defined. The 'AJAX' experience is all about providing a rich, integrated experience to the users - consider a wizard where user fills in certain information, now to provide the page with no flicker capability and also to reduce the amount of data being sent over the wire, the page developer wraps the wizard with an UpdatePanel, now let's assume that while a user is going through the wizard adding information they encounter a server error on the fifth step. If the custom errors were turned ON, redirection would happen to the customer error page - if the user hit 'Back' in the browser they would be directed to the first step in the wizard as the async post-back do not add to browser history. Thus you can set the 'AllowCustomErrorRedirects' property to false to prevent redirection and send the error to the client where it can be handled by the application. The ScriptManager control also provides for script registration methods which allow for scripts to be tracked based on the control which rendered these

   public void RegisterClientScriptBlock(Control control, Type type, string key, string script, bool addScriptTags)

    public void RegisterClientScriptInclude(Control control, Type type, string key, string url)
    public void RegisterClientScriptResource(Control control, Type type, string resourceName)
    public void RegisterHiddenField(Control control, string HiddenFieldName, string HiddenFieldValue) 
    public void RegisterOnSubmitStatement(Control control, Type type, string key, string script)

     public void RegisterStartUpScript(Control control, Type type, string key, string script, bool addScriptTags) 

As can be noticed hte script registration methods take the control instance as a parameter which helps in associating controls with the script they registered and send these scripts to the client if the control was rendered during an async post-back.

The ScriptManager also allows for sending additional data to the client during async poast-back [RegisterDataItem(Control, string)] and registering dispose functions which are called when a control/component is disposed on the client during async post-back as new content replaces the old. The ScriptManager also provides the functionality to fall back to regular post-backs if a browser does not support the expected version of w3c DomVersion, ecmaScript and xmlhttp. This can also be controlled by the user via the SupportsPartialRendering property or by the browser capapbilities mentioned above.

 The object on the client which handles the async post-back is the PageRequestManager - this can be found in the webforms.js that ship with ASP.Net AJAX 1.0. When a user interacts with a page to cause a post-back and the control that caused the post-back is either a child of an UpdatePnael on the page or is registered as an async post-back control then this will cause an async post-back. The PageRequestManager uses the client-side Networking objects to set up a request which is sent to the server via xmlhttp. The PageRequestManager then waits for a callback from the server and this contains the response generated on the server. The PageRequestManager parses the response from the server and then raises the remaining lifecycle of events and ultimately updates the DOM on the client to reflect the rendering updates from the async post-back. The following image displays the client-side events raised during an async post-back

Here are some more details regarding the events

initializeRequest event: can be used for canceling a new async post-back request if one is on-going. Also the post-back source can be evaluated to do any additional work

public class InitializeRequestEventArgs : CancelEventArgs {

bool cancel { get; set; };

DOMElement postBackElement { get; };

Sys.Net.WebRequest request {get; };

}

beginRequest event: can be used for handling UpdateProgress, start showing progress in this event and hide it in the endRequest event

public class BeginRequestEventArgs : EventArgs {

Sys.Net.WebRequest request {get; };

DOMElement postBackElement { get; };

}

pageLoading event: can be used to do additional handling for the panels being updated or deleted. Hook-up for doing clean up logic. Also additional data sent from the server can be inspected for doing customizations.

public class PageLoadingEventArgs : EventArgs {

Dictionary dataItems { get; };

DOMElement[] panelsDeleting { get; };

DOMElement[] panelsUpdating { get; };

}

pageLoaded event: similar to pageLoading, though in this case you have knowledge on any additional UpdatePanels that were created due to the async post-back

public class PageLoadedEventArgs : EventArgs {

Dictionary dataItems { get; };

DOMElement[] panelsCreated { get; };

DOMElement[] panelsUpdated { get; };

}

endRequest event: Can be used to customize error handling, and also to process additional data form the server. Finally endRequest can be used to manage the UpdateProgress hiding.

public class EndRequestEventArgs : EventArgs {

Dictionary dataItems { get; };

Error error { get; };

bool errorHandled { get; set; };

Sys.Net.WebRequestExecutor response {get; };

}

The following sample attaching to the events

<span ID="PRM_Events" >PageRequestManagerEvents:</span>
<script type="text/javascript" >
function beginRequestEventHandler(sender, arg)
{
document.getElementById("PRM_Events").innerHTML = "[beginRequest]";
}

 function endRequestEventHandler(sender, arg)
{
document.getElementById("PRM_Events").innerHTML += "[endRequest][endRequestHasError:";
var errorObject = arg.get_error();
if(errorObject != null)
{
document.getElementById("PRM_Events").innerHTML += "true]";
arg.set_errorHandled(true);
}
else
{
document.getElementById("PRM_Events").innerHTML += "false]";
}
}

function printArray(name, arr)
{
var panels = name + "=" + arr.length;
if(arr.length > 0)
{
panels += "(";
for(var i = 0; i < arr.length; i++)
{
panels += arr[i].id + ",";
}
panels = panels.substring(0, panels.length - 1);
panels += ")";
}
return panels;
}

function pageLoadingEventHandler(sender, arg)
{
var updatedPanels = printArray("PanelsUpdating", arg.get_panelsUpdating());
var deletedPanels = printArray("PanelsDeleting", arg.get_panelsDeleting());
var message = "[pageLoading:" + updatedPanels + ";" + deletedPanels + "]";

document.getElementById("PRM_Events").innerHTML += message;
}

function pageLoadedEventHandler(sender, arg)
{
var updatedPanels = printArray("updatedPanels", arg.get_panelsUpdated());
var createdPanels = printArray("createdPanels", arg.get_panelsCreated());

var message = "[pageLoaded:" + updatedPanels + ";" + createdPanels + "]";

document.getElementById("PRM_Events").innerHTML += message;
}

Sys.WebForms.PageRequestManager.instance.add_beginRequest(beginRequestEventHandler);
Sys.WebForms.PageRequestManager.instance.add_pageLoading(pageLoadingEventHandler);
Sys.WebForms.PageRequestManager.instance.add_pageLoaded(pageLoadedEventHandler);
Sys.WebForms.PageRequestManager.instance.add_endRequest(endRequestEventHandler);
</script> 

Finally, lets look at the updatePanel control itself. The UpdatePanel marks regions of the page which are rendered during an async post-back. The UpdatePanels that are updating can be further filtered by setting the UpdateMode property on the UpdatePanel to 'Conditional'. The default is 'Always'. When set to conditional the UpatePanel updates if any control within it causes a post-back or a trigger for the update panel causes a post-back. In the sample below clicking on 'Button1' causes UP1 to be updated. The 'ControlID' in a trigger can point to a control which implement either IPostBackEventHandler, or IPostBackDataHandler or INamingContainer - if it points to a control that implements INamingContainer then any post-back controls inside this control will act as triggers for the UpdatePanel.

<asp:button runat="server" id="Button1" Text="Update" />

<asp:UpdatePanel

    ID="UP1"

    UpdateMode="Conditional"

    runat="server"

>

    <ContentTemplate>…</ContentTemplate>

    <Triggers>

        <asp:AsyncPostbackTrigger

            ControlID="Button1"

        />

  </Triggers>

</asp:UpdatePanel>

In closing, I am attaching some demo samples for the webcast (most of which were written by one of our devs Eilon - Thanks Eilon) and also the slidedeck with this post. Hopefully you find this useful.

 Please post any feedback you might have and I will try my best to respond.

 Thanks,

Kashif 

 

 

PanelProgress.zip