WEBSITE

ASP.NET AJAX Extensions : Selective Page Updates with Partial Rendering

10/10/2010 11:22:48 AM
AJAX is not a particular technology or product. It refers to a number of client features, and related development techniques, that make Web applications look like desktop applications. AJAX doesn’t require any plug-in modules either and is not browser specific. Virtually any browser released in the past five years can serve as a great host for AJAX-based applications. AJAX development techniques revolve around one common software element—the XMLHttpRequest object. The availability of this object in the object model of most browsers is the key to the current ubiquity and success of AJAX applications. In addition to XMLHttpRequest, a second factor contributes to the wide success of AJAX—the availability of a rich document object model in virtually any browser.

Based on this quick assay of the AJAX paradigm, the programming model of AJAX applications seem to be clear and unquestionable. You write code that captures client-side events, conduct an operation on the server via XMLHttpRequest, get the results, and update the user interface. All the client-side programming is done through JavaScript. This model is a real performance booster when applied to individual features or bottlenecks in existing pages, but it’s hard to scale to a large application because it proves quite expensive in terms of skills to acquire and time to implement.

It is the downside of the loudly requested change of paradigm for Web applications. When it comes to rewriting Web applications for AJAX, nearly all aspects of the application need to be redesigned, refactored, and rewritten. Opting for AJAX all the way might be too much for too many companies; and it’s not a step to take with a light heart.

Today you do much of your ASP.NET programming using server controls. A server control normally emits HTML markup. In an AJAX scenario, a server control emits markup plus some script code to support AJAX requests. This is not exactly the loudly requested change of paradigm, but it is a good compromise between the today’s Web and AJAX. Most third-party vendors prepared their own offering based on this idea. They just provide you with a new set of controls that supply a server and client programming model and manage any browser-to-server communication for you.

But what if you aren’t using any third-party library? Should you write new AJAX-enabled controls yourself? An AJAX server control can be the AJAX version of a traditional server control—for example, an AJAX-enabled drop-down list that supports client insertions and moves them back to the server without a full-page postback. But it can also be a generic control container that takes care of refreshing all of its children without a full-page postback. Enter partial rendering.

ASP.NET partial rendering works according to this idea. It provides a new container control—the UpdatePanel control—that you use to surround portions of existing pages, or portions of new pages developed with the usual programming model of ASP.NET 2.0. A postback that originates within any of these updatable regions is managed by the UpdatePanel control and updates only the controls in the region.

The UpdatePanel Control

In ASP.NET AJAX, partial rendering is the programming technique centered around the UpdatePanel control. In ASP.NET, the UpdatePanel control represents the shortest path to AJAX. It allows you to add effective AJAX capabilities to sites written according to the classic programming model of ASP.NET 2.0. As a developer, you have no new skills to learn, except the syntax and semantics of the UpdatePanel control. The impact on existing pages is very limited, and the exposure to JavaScript is very limited, and even null in most common situations.

You might wonder how partial rendering differs from classic postbacks. The difference is in how the postback is implemented—instead of letting the browser perform a full-page refresh, the UpdatePanel control intercepts any postback requests and sends an out-of-band request for fresh markup to the same page URL. Next, it updates the DOM tree when the response is ready. Let’s investigate the programming interface of the control.

The UpdatePanel Control at a Glance

The UpdatePanel control is a container control defined in the System.Web.Extensions assembly. It belongs specifically to the System.Web.UI namespace. The control class is declared as follows:

public class UpdatePanel : Control
{
...
}

Although it’s logically similar to the classic ASP.NET Panel control, the UpdatePanel control differs from the classic panel control in a number of respects. In particular, it doesn’t derive from Panel and, subsequently, it doesn’t feature the same set of capabilities as ASP.NET panels, such as scrolling, styling, wrapping, and content management.

The UpdatePanel control derives directly from Control, meaning that it acts as a mere AJAX-aware container of child controls. It provides no user-interface-related facilities. Any required styling and formatting should be provided through the child controls. In contrast, the control sports a number of properties to control page updates and also exposes a client-side object model. Consider the following classic ASP.NET code:

<asp:GridView ID="GridView1" runat="server"
DataSourceID="ObjectDataSource1"
AllowPaging="True"
AutoGenerateColumns="False" Width="450px">
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID">
<ItemStyle Width="70px" />
</asp:BoundField>
<asp:BoundField DataField="CompanyName" HeaderText="Company">
<ItemStyle Width="300px" />
</asp:BoundField>
<asp:BoundField DataField="Country" HeaderText="Country">
<ItemStyle Width="80px" />
</asp:BoundField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
TypeName="Core35.DAL.Customers"
SelectMethod="LoadAll" />

This code causes a postback each time you click to view a new page, edit a record, or sort by a column. As a result, the entire page is redrawn even though the grid is only a small fragment of it. With partial rendering, you take the preceding markup and just wrap it with an UpdatePanel control, as shown here:

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
...
</ContentTemplate>
</asp:UpdatePanel>

In addition, you need to add a ScriptManager control to the page. That’s the essence of partial rendering. And it magically just works. Well, not just magically, but it works.

Note

From this simple but effective example, you might be led to think that it suffices that you surround the whole body of the page with an UpdatePanel control and you’re done. If you do, it certainly works. It might not be particularly efficient though. In the worst case, you need the same bandwidth as you do with classic ASP.NET; however, you still give your users an infinitely better experience because only a portion of the page actually refreshes.

As we’ll learn in the rest of the chapter, partial rendering offers a number of attributes to optimize the overall behavior and performance. However, the majority of users are more than happy with the sole effect of a partial page rendering.


The Programming Interface of the Control

Table 1 details the properties defined on the UpdatePanel control that constitute the aspects of the control’s behavior that developers can govern.

Table 1. Properties of the UpdatePanel Control
PropertyDescription
ChildrenAsTriggersIndicates whether postbacks coming from child controls will cause the UpdatePanel to refresh. This property is set to true by default. When this property is false, postbacks from child controls are ignored. You can’t set this property to false when the UpdateMode property is set to Always.
ContentTemplateA template property, defines what appears in the UpdatePanel when it is rendered.
ContentTemplateContainerRetrieves the dynamically created template container object. You can use this object to programmatically add child controls to the UpdatePanel.
IsInPartialRenderingIndicates whether the panel is being updated as part of an asynchronous postback. Note that this property is designed for control developers. Page authors should just ignore it.
RenderModeIndicates whether the contents of the panel will be rendered as a block <div> tag or as an inline <span> tag. The feasible values for the property—Block and Inline—are defined in the UpdatePanelRenderMode enumeration. The default is Block.
UpdateModeGets or sets the rendering mode of the control by determining under which conditions the panel gets updated. The feasible values—Always and Conditional—come from the UpdatePanelUpdateMode enumeration. The default is Always.
TriggersDefines a collection of trigger objects, each representing an event that causes the panel to refresh automatically.

A bit more explanation is needed for the IsInPartialRendering read-only Boolean property. It indicates whether the contents of an UpdatePanel control are being updated. From this description, it seems to be a fairly useful property. Nonetheless, if you read its value from within any of the handlers defined in a code-behind class, you’ll find out that the value is always false.

As mentioned, IsInPartialRendering is a property designed for control developers only. So it is assigned its proper value only at rendering time—that is, well past the PreRender event you can capture from a code-behind class. Developers creating a custom version of the UpdatePanel control will likely override the Render method. From within this context, they can leverage the property to find out whether the control is being rendered in a full-page refresh or in a partial rendering operation.

As a page author, if you just need to know whether a portion of a page is being updated as a result of an AJAX postback, you use the IsInAsyncPostBack Boolean property on the ScriptManager control.

Note

Like any other ASP.NET AJAX feature, partial rendering requires a ScriptManager control in the page. It is essential, though, that the EnablePartialRendering property on the manager be set to true—which is the default case. If this property is set to false, the UpdatePanel control works like a regular panel.


Populating the Panel Programmatically

The content of an updatable panel is defined through a template property—the ContentTemplate property. Just like any other template property in ASP.NET controls, ContentTemplate can be set programmatically. Consider the following page fragment:

<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<%-- Left empty deliberately. Will be filled out programmatically --%>
</asp:UpdatePanel>

In the PreInit event of the code-behind page, you can set the ContentTemplate programmatically, as shown here:

protected void Page_PreInit(object sender, EventArgs e)
{
// You could also read the URL of the user control from a configuration file
string ascx = "customerview.ascx";
UpdatePanel1.ContentTemplate = this.LoadTemplate(ascx);
}



You are not allowed to set the content template past the PreInit event. However, at any time before the rendering stage, you can add child controls programmatically. In ASP.NET, to add or remove a child control, you typically use the Controls property of the parent control, as shown here:

UpdatePanel1.Controls.Add(new LiteralControl("Test"));

If you try to add a child control programmatically to the Controls collection of an UpdatePanel—as in the preceding code snippet—all that you get is a runtime exception. You should use the ContentTemplateContainer property instead. The reason is that what you really want to do is add or remove controls to the content template, not to the UpdatePanel directly. That’s why Controls doesn’t work and you have to opt for the actual container of the template. The following code shows how to populate the content template programmatically:

public partial class Samples_Ch19_Partial_Dynamic : System.Web.UI.Page
{
private Label Label1;

protected void Page_Load(object sender, EventArgs e)
{
UpdatePanel upd = new UpdatePanel();
upd.ID = "UpdatePanel1";

// Define the button
Button button1 = new Button();
button1.ID = "Button1";
button1.Text = "What time is it?";
button1.Click += new EventHandler(Button1_Click);

// Define the literals
LiteralControl lit = new LiteralControl("<br>");

// Define the label
Label1 = new Label();
Label1.ID = "Label1";
Label1.Text = "[time]";

// Link controls to the UpdatePanel
upd.ContentTemplateContainer.Controls.Add(button1);
upd.ContentTemplateContainer.Controls.Add(lit);
upd.ContentTemplateContainer.Controls.Add(Label1);

// Add the UpdatePanel to the list of form controls
this.Form.Controls.Add(upd);
}

protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = DateTime.Now.ToShortTimeString();
}
}



You can add an UpdatePanel control to the page at any time in the life cycle. Likewise, you can add controls to an existing panel at any time. However, you can’t set the content template programmatically past the page’s PreInit event.

Master Pages and Updatable Regions

You can safely use UpdatePanel controls from within master pages. Most of the time, the use of updatable panels is easy and seamless. There are a few situations, though, that deserve a bit of further explanation.

If you add a ScriptManager control to a master page, partial rendering is enabled by default for all content pages. In addition, initial settings on the script manager are inherited by all content pages. What if you need to change some of the settings (for example, add a new script file or switch on script localization) for a particular content page? You can’t have a new script manager, but you need to retrieve the original one defined on the master page.

In the content page, you can declaratively reference a ScriptManagerProxy and change some of its settings. The proxy retrieves the script manager currently in use and applies changes to it.

The ScriptManagerProxy control, though, is mostly designed to let you edit the list of scripts and services registered with the manager in a declarative manner, and it doesn’t let you customize, say, error handling or script localization. You can do the same (and indeed much more) by programmatically referencing the script manager in the master page. Here’s how:

protected void Page_Init(object sender, EventArgs e)
{
// Work around the limitations in the API of the ScriptManagerProxy control
ScriptManager.GetCurrent(this).EnableScriptLocalization = true;
}



In the content page, you create a handler for the page’s Init event, retrieve the script manager instance using the static GetCurrent method on the ScriptManager class, and apply any required change.

User Controls and Updatable Regions

User controls provide an easy way to bring self-contained, auto-updatable AJAX components into an ASP.NET page. Because each page can have at most one script manager, you can’t reasonably place the script manager in the user control. That would work and make the user control completely self-contained, but it would also limit you to using exactly one instance of the user control per page. On the other hand, the UpdatePanel control requires a script manager. Multiple script managers, or the lack of at least one script manager, will cause an exception.

The simplest workaround is that you take the script manager out of the user control and place it in the host page. User controls therefore assume the presence of a script manager, and they use internally as many updatable panels as needed:

<asp:ScriptManager runat="server" ID="ScriptManager1" />
<x:Clock runat="server" ID="Clock1" />
<hr />
<x:Clock runat="server" ID="Clock2" />

Note

You can’t call Response.Write from within a postback event handler (for example, Button1_Click) that gets called during an asynchronous AJAX postback. If you do so, you’ll receive a client exception stating that the message received from the server could not be parsed. In general, calls to Response.Write—but also response filters, HTTP modules, or server tracing (Trace=true)—modify the stream returned to the client by adding explicit data that alters the expected format.


Optimizing the Usage of the UpdatePanel Control

Partial rendering divides the page into independent regions, each of which controls its own postbacks and refreshes without causing, or requiring, a full-page update. This behavior is desirable when only a portion—and perhaps only a small portion—of the page needs to change during a postback. Partial updates reduce screen flickering and allow you to create more interactive Web applications. An ASP.NET page can contain any number of UpdatePanel controls.

An UpdatePanel control refreshes its content under the following conditions:

  • When another UpdatePanel control in the same page refreshes

  • When any of the child controls originates a postback (for example, a button click or a change of selection in a drop-down list with AutoPostBack=true)

  • When handling a postback event the page invokes the Update method on the UpdatePanel control

  • When the UpdatePanel control is nested inside another UpdatePanel control and the parent update panel is updated

  • When any of the trigger events for the UpdatePanel occur

You can control these conditions through a number of properties such as UpdateMode, ChildrenAsTriggers, and the collection Triggers. To minimize the total number of postbacks and the amount of data being roundtripped, you should pay a lot of attention to the values you assign to these properties. Let’s delve deeper into this topic.

Configuring for Conditional Refresh

By default, all updatable panels in a page are synchronized and refresh at the same time. To make each panel refresh independently from the others, you change the value of the UpdateMode property. The default value is Always, meaning that the panel’s content is updated on every postback that originates from anywhere in the page, from inside and outside the updatable region.

By changing the value of the UpdateMode property to Conditional, you instruct the updatable panel to update its content only if it is explicitly ordered to refresh. This includes calling the Update method, intercepting a postback from a child control, or any of the events declared as triggers.

Normally, any control defined inside of an UpdatePanel control acts as an implicit trigger for the panel. You can stop all child controls from being triggers by setting the value of ChildrenAsTriggers to false. In this case, a button inside an updatable panel, if clicked, originates a regular full postback.

What if you want only a few controls within an UpdatePanel to act as triggers? You can define them as triggers of a particular UpdatePanel, or you can use the RegisterAsyncPostBackControl method on the ScriptManager class.

The RegisterAsyncPostBackControl method enables you to register controls to perform an asynchronous postback instead of a synchronous postback, which would update the entire page. Here is an example of the RegisterAsyncPostBackControl method:

protected void Page_Load(object sender, EventArgs e)
{
ScriptManager1.RegisterAsyncPostBackControl(Button1);
}

The control object you pass as an argument will be a control not included in any updatable panels and not listed as a trigger. The effects of the postback that originates from the control differ with regard to the number of UpdatePanel controls in the page. If there’s only one UpdatePanel in the page, the script manager can easily figure out which one to update. The following code shows a page whose overall behavior may change if one or two UpdatePanel controls are used.

protected void Button1_Click(object sender, EventArgs e)
{
// If there's only one UpdatePanel in the page, and it includes this Label control,
// the panel is refreshed automatically.
Label1.Text = "Last update at: " + DateTime.Now.ToLongTimeString();

// This Label control, not included in any UpdatePanel, doesn't have its UI
// refreshed. Its state, though, is correctly updated.
Label2.Text = "Last update at: " + DateTime.Now.ToLongTimeString();
}



When multiple panels exist, to trigger the update you have to explicitly invoke the Update method on the panel you want to refresh:

protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "Last update at: " + DateTime.Now.ToLongTimeString();
UpdatePanel1.Update();
}

All controls located inside of an UpdatePanel control are automatically passed as an argument to the RegisterAsyncPostBackControl method when ChildrenAsTriggers is true.

Note

A postback that originates from within an UpdatePanel control is often referred to as an asynchronous postback or an AJAX postback. Generally, these expressions are used to reference a postback conducted via a script taking advantage of XMLHttpRequest.


Programmatic Updates

We have already mentioned the Update method quite a few times. It’s time we learn more about it, starting with its signature:

public void Update()

The method doesn’t take any special action itself, but is limited to requiring that the child controls defined in the content template of the UpdatePanel control be refreshed. By using the Update method, you can programmatically control when the page region is updated in response to a standard postback event or perhaps during the initialization of the page.

An invalid operation exception can be thrown from within the Update method in a couple of well-known situations. One situation is if you call the method when the UpdateMode property is set to Always. The exception is thrown in this case because a method invocation pre-figures a conditional update—you do it when you need it—which is just the opposite of what the Always value of the UpdateMode property indicates. The other situation in which the exception is thrown is when the Update method is called during or after the page’s rendering stage.

So when should you get to use the Update method in your pages?

You resort to the method if you have some server logic to determine whether an UpdatePanel control should be updated as the side effect of an asynchronous postback—whether it is one that originated from another UpdatePanel in the page or a control registered as an asynchronous postback control.

Using Triggers

As mentioned, you can associate an UpdatePanel control with a list of server-side events. Whenever a registered event is triggered over a postback, the panel is updated. Triggers can be defined either declaratively or programmatically. You add an event trigger declaratively using the <Triggers> section of the UpdatePanel control:

<asp:UpdatePanel runat="server" ID="UpdatePanel1">
<ContentTemplate>
...
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger
ControlID="DropDownList1"
EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>

You need to specify two pieces of information for each trigger—the ID of the control to monitor, and the name of the event to catch. It is essential to note that the AsyncPostBackTrigger component can catch only server-side events. Both ControlID and EventName are string properties. For example, the panel described in the previous code snippet is refreshed when any of the controls in the page posts back (that is, its UpdateMode property defaults to Always) or when the selection changes on a drop-down list control named DropDownList1.

Note

Keep in mind that we’re talking about server-side events here. This implies that, in the previous example, the DropDownList1 control must have AutoPostBack equals to true in order to fire a postback.


The event associated with the AsyncPostBackTrigger component triggers an asynchronous AJAX postback on the UpdatePanel control. As a result, the host page remains intact except for the contents of the referenced panel and its dependencies, if any. Usually, the AsyncPostBackTrigger component points to controls placed outside the UpdatePanel. However, if the panel has the ChildrenAsTriggers property set to false, it could make sense for you to define an embedded control as the trigger. In both cases, when a control that is a naming container is used as a trigger, all of its child controls that cause postback behave as triggers.

Note

You can also add triggers programmatically by using the Triggers collection of the UpdatePanel control. The collection accepts instances of the AsyncPostBackTrigger class.


Full Postbacks from Inside Updatable Panels

By default, all child controls of an UpdatePanel that post back operate as implicit asynchronous postback triggers. You can prevent all of them from triggering a panel update by setting ChildrenAsTriggers to false. Note that when ChildrenAsTriggers is false postbacks coming from child controls are processed as asynchronous postbacks and they modify the state of involved server controls, but they don’t update the user interface of the panel.

There might be situations in which you need to perform full, regular postbacks from inside an UpdatePanel control in response to a control event. In this case, you use the PostBackTrigger component, as shown here:

<asp:UpdatePanel runat="server" ID="UpdatePanel1">
<ContentTemplate>
...
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="DropDownList1"
EventName="SelectedIndexChanged" />
<asp:PostBackTrigger ControlID="Button1" />
</Triggers>
</asp:UpdatePanel>

The preceding panel features both synchronous and asynchronous postback triggers. The panel is updated when the user changes the selection on the drop-down list; the whole host page is refreshed when the user clicks the button.

A PostBackTrigger component causes referenced controls inside an UpdatePanel control to perform regular postbacks. These triggers must be children of the affected UpdatePanel.

The PostBackTrigger object doesn’t support the EventName property. If a control with that name is causing the form submission, the ASP.NET AJAX client script simply lets the request go as usual. The ASP.NET runtime then figures out which server postback event has to be raised for the postback control by looking at its implementation of IPostBackEventHandler.

Note

When should you use a PostBackTrigger component to fire a full postback from inside an updatable panel? If you need, say, a button to refresh a given panel, why not list the Click event of the button as an asynchronous trigger and leave the button outside the panel? Especially when complex and templated controls are involved, it might not be easy to separate blocks of user interface in distinct panels and single controls. So the easiest, and often the only, solution is wrapping a whole block of user interface in an updatable panel. If a single control in this panel needs to fire a full postback, you need to use the PostBackTrigger component.


Practical Steps for Adopting Updatable Panels

The UpdatePanel control works with the idea of limiting the refresh of the page to only the portions of it that are touched by the postback. A clear mapping between user actions and portions of the page that are updated consequently is key to successfully adopting the UpdatePanel control in an ASP.NET site.

The first practical step for successfully migrating page behavior to partial rendering entails that you, given the expected behavior of the page, identify the portions of the page subject to refresh. If you have, say, a complex table layout but only a small fragment of only one cell changes in the page lifetime, there’s no reason to keep the whole table in an UpdatePanel control. Only the server-side control that displays the modifiable text should be wrapped by the panel.

The portions of the page that you should consider to be candidates to be wrapped by an UpdatePanel control should be as small as possible. They also should include the minimum amount of markup and ASP.NET controls.

The second step consists of associating each candidate region with a list of refresh conditions. You basically answer the question, “When does this region get updated?” After you have compiled a list of candidate regions, and for each you have a list of refresh events, you’re pretty much done.

The final step is mapping this information to UpdatePanel controls and triggers. If all the regions you have identified are disjointed, you’re fine. If not, you use properties and triggers on the UpdatePanel control to obtain the expected page behavior, thereby minimizing the impact of postbacks and page flickering.

If needed, updatable panels can be nested. There’s no syntax limitation to the levels of nesting allowed. Just consider that any nested panel refreshes when its parent is refreshed regardless of the settings.

Let’s be honest. It might not be a trivial task, and getting a disjoint set of regions is not always possible. However, given the number of properties supported by the UpdatePanel control, there’s always room for a good compromise between user experience and performance.

Giving Feedback to the User

A partial rendering operation still requires a postback; it still uploads and downloads the view state and fires the well-known page life cycle on the server. The benefits of an asynchronous postback lie in the fact that no full-page refresh is required and only a smaller amount of HTML markup is returned to the client. An asynchronous postback might still take a few seconds to complete if the server operation is a lengthy one.

The mechanics of the asynchronous postback keeps the displayed page up and running. So the biggest improvement of AJAX—the continuous feel with the page—can become its major weakness if not handled properly. Having the computer engaged in a potentially long task might be problematic. Will the user resist the temptation of reclicking that button over and over again? Will the user patiently wait for the results to show up? Finally, will the user be frustrated and annoyed by waiting without any clue of what’s going on? After all, if the page is sustaining a full postback, the browser itself normally provides some user feedback that this is happening. Using ASP.NET AJAX, the callback doesn’t force a regular full postback and the browser’s visual feedback system is not called upon to inform the user things are happening.

In the end, AJAX and partial rendering let developers arrange pages that provide “continuous feel” to users and increased responsiveness. The continuous experience, however, raises new issues. Feedback should be given to users to let them know that an operation is taking place. In addition, user-interface elements should be disabled if the user would start new operations by clicking on the element.

ASP.NET AJAX supplies the UpdateProgress control to display a templated content while any of the panels in the page are being refreshed.

The UpdateProgress Control

The UpdateProgress control is designed to provide any sort of feedback on the browser while one or more UpdatePanel controls are being updated. If you have multiple panels in the page, you might want to find a convenient location in the page for the progress control or, if possible, move it programmatically to the right place with respect to the panel being updated. You can use cascading style sheets (CSSs) to style and position the control at your leisure.

The user interface associated with an UpdateProgress control is displayed and hidden by the ASP.NET AJAX framework and doesn’t require you to do any work on your own. The UpdateProgress control features the properties listed in Table 2.

Table 2. Properties of the UpdateProgress Control
PropertyDescription
AssociatedUpdatePanelIDGets and sets the ID of the UpdatePanel control that this control is associated with.
DisplayAfterGets and sets the time in milliseconds after which the progress template is displayed. Set to 500 by default.
DynamicLayoutIndicates whether the progress template is dynamically rendered in the page. Set to true by default.
ProgressTemplateIndicates the template displayed during an asynchronous postback that is taking longer than the time specified through the DisplayAfter property.

An UpdateProgress control can be bound to a particular UpdatePanel control. You set the binding through the AssociatedUpdatePanelID string property. If no updatable panel is specified, the progress control is displayed for any panels in the page. The user interface of the progress bar is inserted in the host page when the page is rendered. However, it is initially hidden from view using the CSS display attribute.

When set to none, the CSS display attribute doesn’t display a given HTML element and reuses its space in the page so that other elements can be shifted up properly. When the value of the display attribute is toggled on, existing elements are moved to make room for the new element.

If you want to reserve the space for the progress control and leave it blank when no update operation is taking place, you just set the DynamicLayout property to false.

Composing the Progress Screen

The ASP.NET AJAX framework displays the contents of the ProgressTemplate property while waiting for a panel to update. You can specify the template either declaratively or programmatically. In the latter case, you assign the property any object that implements the ITemplate interface. For the former situation, you can easily specify the progress control’s markup declaratively, as shown in the following code:

<asp:UpdateProgress runat="server" ID="UpdateProgress1">
<ProgressTemplate>
...
</ProgressTemplate>
</asp:UpdateProgress>

You can place any combination of controls in the progress template. However, most of the time, you’ll probably just put some text there and an animated GIF. (See Figure 1.)

Figure 1. A progress template informing users that some work is occurring on the server


Note that the UpdateProgress control is not designed to be a gauge component, but rather a user-defined panel that the ScriptManager control shows before the panel refresh begins and that it hides immediately after its completion.

Important

If you’re looking for a real gauge bar to monitor the progress of a server-side task, partial rendering and the UpdateProgress control are not the right tools. As we’ll see later in the chapter, polling is one of the main drawbacks of partial rendering and polling is unavoidable for monitoring server tasks from the client.


Client-Side Events for Richer Feedback

Each asynchronous postback is triggered on the client via script. The entire operation is conducted by the PageRequestManager client object, which invokes, under the hood, the XMLHttpRequest object. What kind of control do developers have on the underlying operation? If you manage XMLHttpRequest directly, you have full control over the request and response. But when these key steps are managed for you, there’s not much you can do unless the request manager supports an eventing model.

The Sys.WebForms.PageRequestManager object provides a few events so that you can customize handling of the request and response. Table 3 lists the supported events that signal the main steps around an AJAX postback that partially update a page. The events are listed in the order in which they fire to the client page.

Table 3. Properties of the UpdateProgress Control
EventEvent ArgumentDescription
initializeRequestInitializeRequestEventArgsOccurs before the request is prepared for sending
beginRequestBeginRequestEventArgsOccurs before the request is sent
pageLoadingPageLoadingEventArgsOccurs when the response has been acquired but before any content on the page is updated
pageLoadedPageLoadedEventArgsOccurs after all content on the page is refreshed as a result of an asynchronous postback
endRequestEndRequestEventArgsOccurs after an asynchronous postback is finished and control has been returned to the browser

To register an event handler, you use the following JavaScript code:

var manager = Sys.WebForms.PageRequestManager.getInstance();
manager.add_beginRequest(OnBeginRequest);

The prototype of the event handler method—OnBeginRequest in this case—is shown here:

function beginRequest(sender, args)

The real type of the args object, though, depends on the event data structure. By using any of these events, you can control in more detail the steps of an asynchronous request. Let’s dig out more.

The initializeRequest event is the first in the client life cycle of an asynchronous request. The life cycle begins at the moment in which a postback is made that is captured by the UpdatePanel’s client-side infrastructure. You can use the initializeRequest event to evaluate the postback source and do any additional required work. The event data structure is the InitializeRequestEventArgs class. The class features three properties—postBackElement, request, and cancel.

The postBackElement property is read-only and evaluates to a DomElement object. It indicates the DOM element that is responsible for the postback. The request property (read-only) is an object of type Sys.Net.WebRequest and represents the ongoing request. Finally, cancel is a read-write Boolean property that can be used to abort the request before it is sent.

Immediately after calling the initializeRequest handler, if any, the PageRequestManager object aborts any pending async requests. Next, it proceeds with the beginRequest event and then sends the packet.

When the response arrives, the PageRequestManager object first processes any returned data and separates hidden fields, updatable panels and whatever pieces of information are returned from the server. Once the response data is ready for processing, the PageRequestManager object fires the pageLoading client event. The event is raised after the server response is received but before any content on the page is updated. You can use this event to provide a custom transition effect for updated content or to run any clean-up code that prepares the panels for the next update. The event data is packed in an instance of the class PageLoadingEventArgs. The class has three properties: panelsUpdating, panelsDeleting, and dataItems. The first two are arrays and list the updatable panels to be updated and deleted, respectively.

The pageLoaded event is raised after all content on the page is refreshed. You can use this event to provide a custom transition effect for updated content, such as flashing or highlighting updated contents. The event data is packed in the class PageLoadedEventArgs, which has three properties: panelsUpdated, panelsDeleted, and dataItems. The first two are arrays and list the updatable panels that were just updated and deleted, respectively.

The endRequest event signals the termination of the asynchronous request. You receive this event regardless of the success or failure of the asynchronous postback.

Disabling Visual Elements During Updates

If you want to prevent users from generating more input while a partial page update is being processed, you can also consider disabling the user interface—all or in part. To do so, you write handlers for beginRequest and endRequest events:

<script type="text/javascript">
function pageLoad()
{
var manager = Sys.WebForms.PageRequestManager.getInstance();
manager.add_beginRequest(OnBeginRequest);
manager.add_beginRequest(OnEndRequest);
}
</script>

You typically use the beginRequest event to modify the user interface as appropriate and notify the user that the postback is being processed:

// Globals
var currentPostBackElem;

function OnBeginRequest(sender, args)
{
// Get the reference to the button click (i.e., btnStartTask)
currentPostBackElem = args.get_postBackElement();
if (typeof(currentPostBackElem) === "undefined")
return;
if (currentPostBackElem.id.toLowerCase() === "btnStartTask")
{
// Disable the button
$get("btnStartTask").disabled = true;
}
}

The beginRequest handler receives event data through the BeginRequestEventArgs data structure—the args formal parameter. The class features only two properties—request and postBackElement. The properties have the same characteristics of analogous properties on the aforementioned InitializeRequestEventArgs class.

In the preceding code snippet, I disable the clicked button to prevent users from repeatedly clicking the same button.

At the end of the request, any temporary modification to the user interface must be removed. So animations must be stopped, altered styles must be restored, and disabled controls must be re-enabled. The ideal place for all these operations is the endRequest event. The event passes an EndRequestEventArgs object to handlers. The class has a few properties, as described in Table 4.

Table 4. Properties of the EndRequestEventArgs Control
PropertyDescription
dataItemsReturns the client-side dictionary packed with server-defined data items for the page or the control that handles this event. (More on registering data items later.)
ErrorReturns an object of type Error that describes the error (if any) that occurred on the server during the request.
errorHandledGets and sets a Boolean value that indicates whether the error has been completely handled by user code. If this property is set to true in the event handler, no default error handling will be executed by the ASP.NET AJAX client library.
ResponseReturns an object of type Sys.Net.WebRequestExecutor that represents the executor of the current request. Most of the time, this object will be an instance of Sys.Net.XMLHttpExecutor.

As you can see, when the endRequest event occurs there’s no information around about the client element that fired the postback. If you need to restore some user interface settings from inside the endRequest event handler, you might need a global variable to track which element caused the postback:

function OnEndRequest(sender, args)
{
if (typeof(currentPostBackElem) === "undefined")
return;
if (currentPostBackElem.id.toLowerCase() === "btnStartTask")
{
$get("btnStartTask").disabled = false;
}
}

Wouldn’t it be nice if you could visually notify users that a certain region of the screen has been updated? As we’ve seen, partial rendering improves the user experience with pages by eliminating a good number of full refreshes. If you look at it from the perspective of the average user, though, a partial page update doesn’t have a clear start and finish like a regular Web roundtrip. The user doesn’t see the page redrawn and might not notice changes in the user interface. A good pattern is employing a little animation to show the user what has really changed with the latest operation. You can code this by yourself using the pair of beginRequest and endRequest events, or you can resort to a specialized component—an UpdatePanel extender control—as we’ll see in a moment.

Important

The disabled HTML attribute works only on INPUT elements. It has no effect on hyperlinks and <a> tags. If you plan to use LinkButtonfalse. Another effective trick might be to cover the area to be disabled with a partially opaque DIV. controls, you have to resort to other JavaScript tricks to disable the user interface. One possible trick is temporarily replacing the onclick handler of the hyperlink with a return value of


Aborting a Pending Update

A really user-friendly system always lets its users cancel a pending operation. How can you obtain this functionality with an UpdateProgress control? The progress template is allowed to contain an abort button. The script code injected in the page will monitor the button and stop the ongoing asynchronous call if it’s clicked. To specify an abort button, you add the following to the progress template:

<input type="button" onclick="abortTask()" value="Cancel" />

In the first place, the button has to be a client-side button. So you can express it either through the <input> element or the <button> element for the browsers that support this element. If you opt for the <input> element, the type attribute must be set to button. The script code you wire up to the onclick event is up to you, but it will contain at least the following instructions:

<script type="text/JavaScript">
function abortTask()
{
var manager = Sys.WebForms.PageRequestManager.getInstance();
if (manager.get_isInAsyncPostBack())
manager.abortPostBack();
}
</script>

You retrieve the instance of the client PageRequestManager object active in the client page and check whether an asynchronous postback is going on. If so, you call the abortPostBack method to stop it.

Important

Canceling an ongoing update in this way is equivalent to closing the connection with the server. No results will ever be received, and no updates will ever occur on the page. However, canceling the update is a pure client operation and has no effect over what’s happening on the server. If the user started a destructive operation, the client-side Cancel button can just do nothing to stop that operation on the server.


Light and Shade of Partial Rendering

Partial rendering is definitely the easiest way to add AJAX capabilities to an ASP.NET Web site. It has a relatively low impact on the structure of existing pages, doesn’t require significant new skills, doesn’t require exposure to JavaScript, and leaves the application model intact. Advocates of a pure AJAX approach might say that there’s no AJAX at all in partial rendering. And such a statement is not false, indeed.

Haven’t we said that AJAX is key because it propounds a new programming paradigm for building Web applications? And now we’re back to giving kudos to partial rendering—an approach that admittedly maintains the old programming model of classic Web applications? What’s the point?

Overall, partial rendering is only one possible way to approach AJAX. It preserves most of your current investments and is relatively cheap to implement. Partial rendering just adds AJAX capabilities to your pages. It doesn’t constitute a true AJAX application. There’s no architectural new point in partial rendering. It’s a great technique to quickly update legacy applications, and it is an excellent choice when you lack the time, skills, or budget to move on and redesign the application. But in a good number of cases, an improved user interface and optimized rendering is all that your users demand. So partial rendering would perfectly fit in.

On the other hand, building true AJAX applications where all the presentation logic lives on the client written in JavaScript is not trivial either, no matter how much help third-party libraries might offer.

In the end, you should be aware of the structural limitations that partial rendering has. You might want to start with partial rendering to improve your pages and then move on to other, more purely AJAX, solutions to fix particular bottlenecks that still remain. My advice is that a pure AJAX approach where a lot of JavaScript is involved is a solution that should be considered carefully. And that you should have good reasons for both adopting or refusing it.

Note

Why is it so darned hard to write pure AJAX applications? AJAX applications are all about the client, and the client is JavaScript and HTML. Both have significant limitations in light of the complexity of applications these days. JavaScript is an interpreted language, and it does not have a particularly modern syntax. Additionally, JavaScript is subject to the implementation that browsers provide. So a feature might be flaky in one browser and super-optimized in another. Originally born as a document format, HTML is used more as an application delivery format. But for this purpose, HTML is simply inadequate because it lacks strong and built-in graphics and layout capabilities. Silverlight 2.0 with its embedded common language runtime (CLR), support for managed languages, and full support for Windows Presentation Foundation (WPF) seems to address both issues.


Issues with Concurrent Calls

Partial rendering has a number of positives, but it also has a couple of key drawbacks. In particular, it doesn’t support concurrent asynchronous postbacks. This means that you are not allowed to have two asynchronous postbacks going on at the same time. Partial rendering bypasses the standard browser’s mechanism that handles an HTTP request. It hooks up the submit event of the form, cuts the standard browser handler out, and finally places the HTTP request using XMLHttpRequest.

The request that reaches the Web server differs from a regular ASP.NET request only for an extra HTTP header. The request sends in the contents of the posting form, including the view-state hidden field. The response is not pure HTML but represents a text record where each field describes the new status of a page element—update panels, hidden fields, scripts to run on loading.

As you can see, the underlying model of partial rendering is still the model of classic ASP.NET pages. It is a sort of stop-and-go model where the users posts back, waits for a while, and then receives a new page. While waiting for the next page, there’s not much the user can do. Only one server operation per session occurs at a time. Partial rendering is only a smarter way of implementing the old model.

From a technical standpoint, the major factor that prevents multiple asynchronous postbacks is the persistence of the view-state information. When two requests go, both send out the same copy of the view state, but each reasonably returns a different view state. Which one has to be good for the page then?

Is dropping the view state entirely an option, at least for asynchronous postbacks? Whatever way you look at it, dropping the view state increases the amount of JavaScript needed for each page. The view state allows you to keep the server logic in C# or Visual Basic .NET and generate the user interface through server controls. This is not to say, though, that another approach isn’t possible. Anyway, partial rendering works this way.

Whenever a request for an asynchronous postback is raised, the PageRequestManager class checks whether another operation is pending. If so, by default, it silently kills the ongoing request to make room for the new one—a last-win discipline.

This fact has a clear impact on developers. In fact, you should always modify the user interface to ensure that users can’t start a second operation before the first is terminated. Otherwise, the first operation is aborted in favor of the second. This happens in any case, even when the two operations are logically unrelated.

Note

When concurrent calls are necessary, you should consider moving that page (if not the whole application) to a more AJAX-oriented design. Alternatively, you can consider implementing that feature within the page using some of the features covered in the next chapter, such as page methods or script services.


Issues with Polling

Among other things, AJAX pages are popular because they can bring on the client information in a timely manner. A page starts polling a remote URL, grabs fresh information, and returns it to the client for the actual display. Implemented via partial rendering, polling is subject to being interrupted when the user starts a new partial rendering operation to restart automatically at the end.

If this is not a problem for you, you can use the new Timer server control, as shown here:

<asp:Timer ID="Timer1" runat="server" Enabled="true" Interval="1000" ontick="Timer1_Tick" />
<asp:Button ID="Button1" runat="server" Text="Start task" onclick="Button1_Click" />
<asp:UpdateProgress ID="UpdateProgress1" runat="server" DynamicLayout="false">
<ProgressTemplate>
<img src="loading.gif" />
</ProgressTemplate>
</asp:UpdateProgress>

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Label1" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
</asp:UpdatePanel>

<hr />

<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<asp:Label ID="lblClock" runat="server" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
</Triggers>
</asp:UpdatePanel>



The Timer control is the server counterpart of a client timer created using the window.setTimeout method. In the preceding code, the Timer control causes a postback every second as specified by the Interval property. The postback fires the Tick event. By using the timer as the trigger of an updatable panel, you can refresh the content of the panel periodically. In the code, the second UpdatePanel control just renders out a digital clock:

protected void Timer1_Tick(object sender, EventArgs e)
{
// Update the clock
lblClock.Text = DateTime.Now.ToString();
}

As in Figure 2, the clock stops working while the remote task triggered by the other button is still running.

Figure 2. Multiple calls are not allowed in a partial rendering page

Other  
 
Most View
Dell Insporon 14R 5421 Touch - Touchscreen Laptop With Durable Battery (Part 1)
MSI GX60 Review - Radeon HD 7970M In A $1,200 Gaming Notebook (Part 9)
Samsung Chromebook - Is It Worth The Value Equation (Part 3)
6 Best New Things To Do With Raspberry Pi (Part 1)
The JauBird BlueBuds X - Wireless Comforts For Yuppies
TaskPaper - Completely Rethinks Lists
Corsair Force Series GS 240GB - Blessed With Toggle-Mode NAND
17 Killer Mac Apps Under $20 (Part 4) : Typeit4me, ReadKit, Doo document organizer
Haswell Ultrabooks Shootout Featherweight Battle Royale (Part 3) - Sony VAIO Pro 13, Toshiba PORTEGE Z30
Olympus M.Zuiko Digital ED 75-300mm f/4.8-6.7 II Lens Review
Top 10
Sharepoint 2013 : Farm Management - Disable a Timer Job,Start a Timer Job, Set the Schedule for a Timer Job
Sharepoint 2013 : Farm Management - Display Available Timer Jobs on the Farm, Get a Specific Timer Job, Enable a Timer Job
Sharepoint 2013 : Farm Management - Review Workflow Configuration Settings,Modify Workflow Configuration Settings
Sharepoint 2013 : Farm Management - Review SharePoint Designer Settings, Configure SharePoint Designer Settings
Sharepoint 2013 : Farm Management - Remove a Managed Path, Merge Log Files, End the Current Log File
SQL Server 2012 : Policy Based Management - Evaluating Policies
SQL Server 2012 : Defining Policies (part 3) - Creating Policies
SQL Server 2012 : Defining Policies (part 2) - Conditions
SQL Server 2012 : Defining Policies (part 1) - Management Facets
Microsoft Exchange Server 2010 : Configuring Anti-Spam and Message Filtering Options (part 4) - Preventing Internal Servers from Being Filtered