2.3. ASP.NET AJAX's Server-Side Controls

The AJAX Extensions section in the Visual Studio 2008 toolbox, provides few controls for your use. Those controls that are there are focused on allowing you to AJAX-enable your ASP.NET applications. They are enabling controls. If you are looking for more specific server controls that take advantage of the new AJAX model, then look at the ASP.NET AJAX Control Toolkit — a separate download that is covered in Chapter 6.

The ASP.NET AJAX server controls that come with ASP.NET 3.5 are laid out in the following table.

ASP.NET AJAXServer ControlDescription
ScriptManagerA component control that manages the marshalling of messages to the AJAX-enabled server for the parts of the page requiring partial updates. To work, every ASP.NET page requires a ScriptManager control. It is important to note that you can have only a single ScriptManager control on a page.
ScriptManagerProxyA component control that acts as a ScriptManager control for a content page. The ScriptManagerProxy control, which sits on the content page (or subpage), works in conjunction with a required ScriptManager control that resides on the master page.
TimerThe Timer control executes client-side events at specific intervals and allows specific parts of your page to update or refresh at these moments.
UpdatePanelA container control that allows you to define specific areas of the page that are enabled to work with the ScriptManager. These areas can then, in turn, make the partial page postbacks and update themselves outside the normal ASP.NET page postback process.
UpdateProgressA control that allows you to display a visual element to the end user to show that a partial-page postback is occurring to the part of the page making the update. This is an ideal control to use when you have long-running AJAX updates.

The next few sections of this chapter look at these new controls and how to use them within your ASP.NET pages.

2.3.1. The ScriptManager Control

Probably the most important control in your ASP.NET AJAX arsenal is the ScriptManager server control, which works with the page to allow for partial page rendering. You use a single ScriptManager control on each page that you want to use the AJAX capabilities provided by ASP.NET 3.5. When placed in conjunction with the UpdatePanel server control, AJAX-enabling your ASP.NET applications can be as simple as adding two server controls to the page and then you are ready to go!

The ScriptManager control takes care of managing the JavaScript libraries that are utilized on your page as well as marshaling the messages back and forth between the server and the client for the partial page rendering process. The marshaling of the messages can be done using either SOAP or JSON through the ScriptManager control. By default, JSON is being used.

If you place only a single ScriptManager control on your ASP.NET page, it takes care of loading the JavaScript libraries needed by ASP.NET AJAX. The page for this is presented in Listing 2-10.

Example 2-10. An ASP.NET page that includes only the ScriptManager control
<%@ Page Language="C#" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>The ScriptManager Control</title>
</head>
<body>
    <form id="form1" runat="server">

<div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
    </div>
    </form>
</body>
</html>

From Listing 2-10, you can see that this control is like all other ASP.NET controls and needs only an ID and a runat attribute to do its work. The page output from this bit of ASP.NET code is presented in Listing 2-11.

Example 2-11. The page output from the ScriptManager control
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>
 The ScriptManager Control
</title></head>
<body>
    <form name="form1" method="post" action="Default2.aspx" id="form1">
<div>
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
 value="/wEPDwULLTEzNjQ0OTQ1MDdkZO9dCw2QaeC4D8AwACTbOkD1OX4h" />
</div>

<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}
//]]>
</script>

<script src="/AJAXWebSite/WebResource.axd?d=o84znEj-
 n4cYi0Wg0pFXCg2&amp;t=633285028458684000" type="text/javascript"></script>

<script src="/AJAXWebSite/ScriptResource.axd?d=
 FETsh5584DXpx8XqIhEM50YSKyR2GkoMoAqraYEDU5_gi1SUmL2Gt7rQTRBAw56lSojJR
 Qe0OjVI8SiYDjmpYmFP0CO8wBFGhtKKJwm2MeE1&amp;t=633285035850304000"
 type="text/javascript"></script>

<script type="text/javascript">
//<![CDATA[
if (typeof(Sys) === 'undefined') throw new Error('ASP.NET AJAX client-side
 framework failed to load.'),
//]]>
</script>

<script src="/AJAXWebSite/ScriptResource.axd?d=FETsh5584DXpx8XqIhEM50YSKyR2GkoMo
 AqraYEDU5_gi1SUmL2Gt7rQTRBAw56l7AYfmRViCoO2lZ3XwZ33TGiCt92e_UOqfrP30mdEYnJYs09ul
 U1xBLj8TjXOLR1k0&amp;t=633285035850304000" type="text/javascript"></script>
    <div>
        <script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1',
 document.getElementById('form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls([], [], [], 90);
//]]>
</script>
    </div>

<script type="text/javascript">
//<![CDATA[
Sys.Application.initialize();
//]]>
</script>

</form>
</body>
</html>

The page output shows that a number of JavaScript libraries are loaded with the page. You will also notice that the script sources are dynamically registered and available through the HTTP handler provided through the ScriptResource.axd handler.

If you are interested in seeing the contents of the JavaScript libraries, you can use the src attribute's URL in the address bar of your browser and you will be prompted to download the JavaScript file that is referenced. You will be prompted to save the ScriptResource.axd file, but you can rename it to make use of a .txt or .js extension if you wish.

An interesting point about the ScriptManager is that it deals with the scripts that are sent to the client by taking the extra step to compress them.

2.3.2. The ScriptManagerProxy Control

The ScriptManagerProxy control was actually introduced to work specifically with the master page capability that ASP.NET offers. As with the ScriptManager control in the previous section, you need a single ScriptManager control on each page that is going to be working with ASP.NET AJAX. However, with that said, the big question is what do you do when you are utilizing master pages. Do you need to put the ScriptManager control on the master page and how does this work with the content pages that use the master page?

When you create a new master page from the Add New Item dialog, in addition to an option for a Master Page, there is also an option to add an AJAX Master Page. This option creates the page shown in Listing 2-12.

Example 2-12. The AJAX Master Page
<%@ Master Language="C#" %>

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">

        </asp:ContentPlaceHolder>
    </div>
    </form>
</body>
</html>

This code shows that there is indeed a ScriptManager control on the page and that this control will be added to each and every content page that uses this master page. You do not have to do anything special to a content page to use the ASP.NET AJAX capabilities provided by the master page. Instead, you can create a content page that is no different from any other content page that you might be used to creating.

However, if you are going to want to modify the ScriptManager control that is on the master page in any way, then you have to add a ScriptManagerProxy control to the content page, as shown in Listing 2-13.

Example 2-13. Adding to the ScriptManager control from the content page
<%@ Page Language="C#" MasterPageFile="~/AJAXMaster.master" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1"
 Runat="Server">
 <asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
     <Scripts>
         <asp:ScriptReference Path="myOtherScript.js" />
     </Scripts>
 </asp:ScriptManagerProxy>
</asp:Content>

In this case, the content page adds to the ScriptManager control that is on the master page by interjecting a script reference from the content page. If you use a ScriptManagerProxy control on a content page and there does not happen to be a ScriptManager control on the master page, you will get an error.

2.3.3. The UpdatePanel Control

The UpdatePanel server control is an AJAX-specific control that is new in ASP.NET 3.5. The UpdatePanel control is the control that you are likely to use the most when dealing with AJAX. This control preserves the postback model and allows you to perform a partial page render.

The UpdatePanel control is a container control, which means that it does not actually have UI-specific items associated with it. It allows you to mark page regions as eligible to be refreshed independently. It is a way to trigger a partial page postback and update only the portion of the page that the UpdatePanel specifies.

When a control within the UpdatePanel control triggers a postback, the UpdatePanel intervenes to initiate the postback asynchronously and update just that portion of the page. The term asynchronously means that end users do not have to stop and wait for the result from the server. Rather, they are able to continue servicing the page with other JavaScript code and can interact with other controls while waiting for the response from the server.

When the application makes the call to the server, the application provides the name of a JavaScript callback function that is called when the response has been received. That callback function receives the results and updates various page controls accordingly.

2.3.3.1. The <ContentTemplate> and <Triggers> Element

There are a couple of ways to deal with the controls on the page that initiate the asynchronous page postbacks. The first is by far the simplest and is shown in Listing 2-14.

Example 2-14. Putting the triggers inside the UpdatePanel control
<%@ Page Language="C#" %>

<script runat="server">
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = "This button was clicked on " + DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>UpdatePanel Control</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
               <asp:Label ID="Label1" runat="server"></asp:Label>

<br />
                <br />
                <asp:Button ID="Button1" runat="server"
                 Text="Click to initiate async request"
                 OnClick="Button1_Click" />
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

In this case, the Label and Button server controls are contained within the UpdatePanel server control. The <asp:UpdatePanel> element has two possible sub-elements: <ContentTemplate> and the <Triggers> elements. Any content that needs to be changed with the asynchronous page postbacks should be contained within the <ContentTemplate> section of the UpdatePanel control.

By default, any type of control trigger (something that would normally trigger a page postback) that is contained within the <ContentTemplate> section instead causes the asynchronous page postback. That means, in the case of Listing 2-14, the button on the page will trigger an asynchronous page postback instead of a full-page postback. Each click on the button changes the time displayed in the Label control.

This highlights a key point about the UpdatePanel control: When using the UpdatePanel, the code for the page executes just as though it were a regular postback. You can see from the code listing that there was not any code on the page that did anything specific for partial page updates. The code never made reference to the UpdatePanel control, and yet only part of the page changed. The UpdatePanel gathers the rendering from controls within the UpdatePanel and abandons most of the rest of the output. It also returns the updated hidden fields with things like ViewState that need to reflect the new state of the page.

Listing 2-14 demonstrates one of the big issues with this model: When the asynchronous postback occurs, you are not only sending the date/time value for the Label control, but you are also sending back the entire code for the button that is on the page. This occurs within the UpdatePanel control when any of the child controls contained within would normally trigger a postback. This is because the default value for the UpdateMode property of the UpdatePanel control is set to Always, and the default value of the ChildrenAsTriggers property is set to true. When using the default UpdatePanel control in this manner, your postbacks will appear as something similar to what is presented here.

265|updatePanel|UpdatePanel1|
    <span id="Label1">This button was clicked on 07/3/2008 5:44:25 AM</span>
    <br />
    <br />
    <input type="submit" name="Button1" value="Click to initiate async request"
     id="Button1" />
            |164|hiddenField|__VIEWSTATE|/wEPDwUKLTU2NzQ4MzIwMw9kFgICBA9kFgICAw9kFg
            JmD2QWAgIBDw8WAh4EVGV4dAUxVGhpcyBidXR0b24gd2FzIGNsaWNrZWQgb24gMTEvMT
            gvMjAwNyAxMTo0NToyMSBBTWRkZKJIG4WwhyQvUwPCX4PxI5FEUFtC|48|hiddenField|
            __EVENTVALIDATION|/wEWAgL43YXdBwKM54rGBlI52OYVl/McOV61BYd/3wSj+RkD|0|
            asyncPostBackControlIDs|||0|postBackControlIDs|||13|updatePanelIDs||
            tUpdatePanel1|0|childUpdatePanelIDs|||12|panelsToRefreshIDs||UpdatePane
            l1|2|asyncPostBackTimeout||90|22|formAction||SimpleUpdatePanel.aspx|11|
            pageTitle||UpdatePanel|

This bit of code which constitutes the response that is sent back to the client via the asynchronous postback shows that the entire section contained within the UpdatePanel control is reissued. You can slim down your pages by including only the portions of the page that are actually updating. If you take the button outside of the <ContentTemplate> section of the UpdatePanel control, then you have to include a <Triggers> section within the control.

The reason for this is that, while the content that you want to change with the asynchronous postback is all contained within the <ContentTemplate> section, you have to tie up a page event to cause the postback to occur. This is how the <Triggers> section of the UpdatePanel control is used. You use this section of the control to specify the various triggers that initiate an asynchronous page postback. Using the <Triggers> element within the UpdatePanel control, you can rewrite Listing 2-14 as shown in Listing 2-15.

Example 2-15. Using a trigger to cause the asynchronous page postback
<%@ Page Language="C#" %>

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = "This button was clicked on " + DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>UpdatePanel</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server"></asp:Label>
            </ContentTemplate>
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
            </Triggers>
        </asp:UpdatePanel>
        <br />
        <br />
        <asp:Button ID="Button1" runat="server"
         Text="Click to initiate async request"
         OnClick="Button1_Click" />
    </div>
    </form>
</body>
</html>

In this case, the Button control and the HTML elements are outside the <ContentTemplate> section of the UpdatePanel control and therefore will not be sent back to the client for each asynchronous page postback. The only item contained in the <ContentTemplate> section is the only item on the page that needs to change with the postbacks — the Label control. Tying this all together is the <Triggers> section.

The <Triggers> section can contain two possible controls: AsyncPostBackTrigger and PostBackTrigger. In this case, the AsyncPostBackTrigger is used. The PostBackTrigger control causes a full page postback, whereas the AsyncPostBackTrigger control causes only an asynchronous page postback (obviously as described by the names of the controls).

As you can see from the example in Listing 2-15, which uses the AsyncPostBackTrigger element, only two attributes are used to tie the Button control to the trigger for the asynchronous postback: the ControlID and the EventName attributes. The control you want to act as the initiator of the asynchronous page postback is put as the value of the ControlID attribute (the control's name as specified by the control's ID attribute). The EventName attribute's value is the name of the event for the control that is specified in the ControlID that you want to be called in the asynchronous request from the client. In this case, the Button control's Click() event is called and this is the event that changes the value of the control that resides within the <ContentTemplate> section of the UpdatePanel control.

Running this page and clicking on the button gives you a smaller asynchronous response back to the client.

108|updatePanel|UpdatePanel1|
   <span id="Label1">This button was clicked on 11/18/2008 11:58:56 AM</span>
            |164|hiddenField|__VIEWSTATE|/wEPDwUKMjA2NjQ2MDYzNw9kFgICBA9kFgICAw9kFg
            JmD2QWAgIBDw8WAh4EVGV4dAUxVGhpcyBidXR0b24gd2FzIGNsaWNrZWQgb24gMTEvMTgv
            MjAwNyAxMTo1ODo1NiBBTWRkZPJA9uj9wwRaasgTrZo85rVvLnoi|48|hiddenField|
            __EVENTVALIDATION|/wEWAgKK3YDTDAKM54rGBqrbjV4/u4ks3aKsn7Xz8xNFE8G/|7|
            asyncPostBackControlIDs||Button1|0|postBackControlIDs|||13|
            updatePanelIDs||tUpdatePanel1|0|childUpdatePanelIDs|||12|
            panelsToRefreshIDs||UpdatePanel1|2|asyncPostBackTimeout||
            90|22|formAction||SimpleUpdatePanel.aspx|11|pageTitle||
            UpdatePanel|

Although not considerably smaller than the previous example, it is smaller and the size similarity is really due to the size of the page used in this example (pages that are more voluminous would show more dramatic improvements). Pages with heavy content associated with them can show some dramatic size reductions, depending on how you structure your pages with the UpdatePanel control.

2.3.3.2. The UpdateMode and ChildrenAsTriggers Properties

Two important properties of the UpdatePanel control that you will find include the UpdateMode and ChildrenAsTriggers properties. The UpdateMode property is of the type UpdatePanelUpdateMode. The two possible values of this property are Always and Conditional. The default mode of Always means that the UpdatePanel will be refreshed anytime a child control initiates a postback. You saw this in action in the previous examples.

This behavior corresponds to the ChildrenAsTriggers property's default value of true. When an UpdatePanel is triggered to refresh, and if the control is using the default settings, the child control's postback will trigger an update. One interesting point is that if you set ChildrenAsTriggers to a value of false without changing the UpdateMode value to Conditional, you will then get the error shown in Figure 2-7.

Figure 2-7. Figure 2-7

The following table summarizes the results of possible combinations of UpdateMode and ChildrenAsTriggers properties.

UpdateModeChildrenAsTriggersResult
AlwaysFalseThis combination is a set of illegal parameters and will result in an exception.
AlwaysTrueIn this combination, the UpdatePanel control will refresh either if the entire page refreshes or if a child control contained within the control initiates a postback.
ConditionalFalseWith these two settings, the UpdatePanel control will refresh if the entire page refreshes or if another control outside the UpdatePanel control triggers a postback. With this combination, none of the nested child controls within the UpdatePanel control will be able to initiate a postback.
ConditionalTrueUsing this combination, the UpdatePanel control refreshes if the entire page refreshes or when a child control within the UpdatePanel controls performs a postback or when a trigger control outside the panel initiates a refresh.

As stated, you cannot set ChildrenAsTriggers to false while the UpdateMode is set to Always. The two property settings are contradictory to each other, as you would be asking the UpdatePanel control to refresh based on the child controls while simultaneously saying that the child controls should not trigger updates. In order for the false setting to work, the UpdateMode property needs to be set to the only other possible setting of Conditional.

When the UpdateMode property is set to a value of Conditional and the ChildrenAsTriggers property is set to a value of false, the UpdatePanel control will not refresh unless the entire page is updated or when a control outside the UpdatePanel is defined as the trigger and initiates a postback. Listing 2-16 illustrates this behavior.

Example 2-16. Using the UpdateMode and ChildrenAsTriggers properties
<%@ Page Language="C#" %>

<script runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        string theTime = DateTime.Now.ToLongTimeString();

        for (int i = 0; i < 3; i++)
        {
            theTime += "<br />" + theTime;
        }

        time1.Text = theTime;
        time2.Text = theTime;
    }

    protected void Button3_Click(object sender, EventArgs e)
    {
        Response.Write("You performed a complete page postback on " +
           DateTime.Now);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>UpdatePanel Triggers</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:Button ID="Button1" runat="server" Text="Update the top UpdatePanel"
            Width="350px" />
        <br />
        <br />
        <asp:Button ID="Button2" runat="server"

Text="Update the bottom UpdatePanel"
            Width="350px" />
        <br />
        <br />
        <asp:Button ID="Button3" runat="server" onclick="Button3_Click"
            Text="Perform a complete page postback" Width="350px" />
        <br />
        <br />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
            </Triggers>
            <ContentTemplate>
                <div style="border-style: solid; background-color: gray;">
                    <asp:Label runat="server" ID="time1"></asp:Label><br />
                    <br />
                </div>
                <br />
            </ContentTemplate>
        </asp:UpdatePanel>
        <asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional"
            ChildrenAsTriggers="false">
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="Button2" EventName="Click" />
            </Triggers>
            <ContentTemplate>
                <div style="border-style: solid; background-color: gray;">
                    <asp:Label runat="server" ID="time2"></asp:Label><br />
                </div>
                <br />
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

This example contains two UpdatePanel controls, each with a Label control that displays the time retrieved from the server (or machine you are working with for your development). The three buttons at the top of the page are outside of the UpdatePanel controls themselves. Running this page, you would get results that are similar to those in Figure 2-8.

Looking back at the code for this page, The UpdateMode property of both UpdatePanel controls is set to a value of Conditional. This example also sets the ChildrenAsTriggers property to false on only the last of the UpdatePanel controls, but in reality it is completely unnecessary in this example as there are not any child controls that cause a postback, so the setting does not affect the behavior.

There is also a button on the page (Button3) that does a complete page postback and in this case, if clicked, it updates the contents of both UpdatePanel controls.

Figure 2-8. Figure 2-8

Usually, update calls made using the UpdatePanel are done asynchronously. However, in some cases you will want to have a full-page postback occur instead. You can make exceptions to the partial page update behavior by defining a PostBackTrigger within the UpdatePanel control. For this behavior requirement, the event name does not need to be defined. Instead, you just provide a value of the ControlID property as is illustrated here in this partial code example.

<Triggers>
   <asp:PostBackTrigger ControlID="Button2" />
</Triggers>

2.3.3.3. Building Triggers Using Visual Studio 2008

If you like to work on the design surface of Visual Studio when building your ASP.NET pages, you will find that there is good support for building your ASP.NET AJAX pages, including the creation of triggers in the UpdatePanel control. To see this in action, place a single UpdatePanel server control on your page and view the control details in the Properties dialog within Visual Studio. The Triggers item in the list has a button next to it that allows you to modify the items associated with it. This is illustrated in Figure 2-9.

Figure 2-9. Figure 2-9

Clicking on the button in the Properties dialog launches the UpdatePanelTrigger Collection Editor, as shown in Figure 2-10. This editor allows you to add any number of triggers and to associate them to a control and a control event very easily. Figure 2-10 assumes you have a button on the page to work with.

Figure 2-10. Figure 2-10

Clicking the OK button here adds the trigger to the <Triggers> section of your UpdatePanel control.

2.3.3.4. Invoking Updates from the Server Using the Update() Method

So far, the examples have all been about controlling partial-page updates from within the browser by allowing the UpdatePanel to work with control triggers such as a button click event. It is important to understand that while you can easily work with the UpdatePanel control in this manner to perform the asynchronous page updates your application requires, it is also just as easy to access the update behaviors for your page through programmatic interaction with the UpdatePanel control itself.

The UpdatePanel control provides an Update() method that allows a region of the page to be refreshed programmatically from server-side code. During a partial page update, remember that the page runs normally and the UpdatePanel controls are just updating without the user seeing a full page postback. Any condition during the page execution can be a reason to update a portion of the page programmatically. Listing 2-17 illustrates an example of this in action.

Example 2-17. Programmatically triggering updates using the Update() method
<%@ Page Language="C#" %>

<script runat="server">
    protected override void OnLoad(EventArgs e)
    {
        var tempTime = Session["LastTime"];

        if (tempTime != null)
        {
            DateTime lastTime = (DateTime)tempTime;

            if (DateTime.Now > lastTime.AddSeconds(4))
            {
                UpdatePanel2.Update();
                Session["lastTime"] = DateTime.Now;
            }
        }
        else
        {
            Session["lastTime"] = DateTime.Now;
        }

        base.OnLoad(e);
        string theTime = DateTime.Now.ToLongTimeString();

        for (int i = 0; i < 3; i++)
        {
            theTime += "<br />" + theTime;
        }

        time1.Text = theTime;
        time2.Text = theTime;
    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Programmatically triggering using the Update method</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:Button ID="Button1" runat="server" Text="Update Conditionally" />
        <br />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
            </Triggers>
            <ContentTemplate>

<div style="border-style: solid; background-color: gray;">
                    <asp:Label runat="server" ID="time1"></asp:Label><br />
                    <br />
                </div>
                <br />
            </ContentTemplate>
        </asp:UpdatePanel>
        <asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                <div style="border-style: solid; background-color: gray;">
                    <asp:Label runat="server" ID="time2"></asp:Label><br />
                </div>
                <br />
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

In Listing 2-17, the page code updating the time is changed to conditionally call the Update() method of the UpdatePanel control of the second UpdatePanel at the same time the first UpdatePanel is being refreshed. The current time is stored in the Session object on each call. When the page executes, the current time is checked against the time stored previously in Session. If this difference in time is more than four seconds, then the Update() method is invoked. The refreshed view of that portion of the page is then also updated when the call completes and the first UpdatePanel is updated. Running this page will give you results similar to those in Figure 2-11.

Figure 2-11. Figure 2-11

2.3.3.5. The RenderMode Property

Although the UpdatePanel does not have a visible rendering of its own, it wraps the rendering of the content placed in the ContentTemplate section in an HTML element. The default behavior is to use a <div> element as the wrapper, but the RenderMode property of the UpdatePanel control allows you to switch to use a <span> element instead. Setting the RenderMode to a value of Block results in the rendering being placed inside a <div> element. Setting RenderMode to Inline indicates that a <span> element should be used.

2.3.3.6. The UpdatePanel Control's Place within the ASP.NET Page Lifecycle

The ASP.NET page execution lifecycle is not altered by the partial rendering feature provided by ASP.NET AJAX. The ScriptManager control participates in the lifecycle to facilitate the partial page updates. It coordinates gathering the renderings from the UpdatePanels that need to be refreshed during an asynchronous post and carrying the hidden fields necessary to make the following post function correctly.

Controls that modify the ViewState on the page, even if they are not in the UpdatePanel being affected, do not have to take any special action to ensure the change is available in subsequent requests. Event validation, cross-page posting, and ASP.NET's ability to maintain the scroll position all continue to work when using a UpdatePanel control on your page.

When using the UpdatePanel control, it is vital to remember that the page lifecycle is the same as it has always been. It is nice that no special pains are required to start taking advantage of partial-page updates, but do not forget that the overhead on the server is the same and that the traffic between browser and server can still be significant. For example, if you have a GridView or other data control on the page that is making use of ViewState to avoid trips to the database, that ViewState will be carried with each request even when the data is not being updated. As this ViewState baggage can slow down partial page updates, you should carefully consider which controls need to have ViewState enabled, and disable it on controls that don't need it.

There are several ways to determine whether your code is executing in the context of a partial-page update. To find information about the current state, the control author can walk up the control tree to determine whether the controls are contained within an UpdatePanel and query the IsInPartialRendering property. This property reveals whether the UpdatePanel is rendering back to the browser with asynchronous updates in order to take custom actions if desired. The ScriptManager control can let you know if the page is executing as the result of an UpdatePanel initiated asynchronous post. To accomplish this, check the IsInAsyncPostBack property to make the determination:

if (ScriptManager1.IsInAsyncPostBack)
{

}

2.3.4. The Timer Control

One common task when working with asynchronous postbacks from your ASP.NET pages is that you might want these asynchronous postbacks to occur at specific intervals. To accomplish this, you use the Timer control available to you from the AJAX Extensions part of the toolbox. A simple example to demonstrate how this control works involves putting some timestamps on your page and setting postbacks to occur at specific timed intervals. This example is illustrated in Listing 2-18.

Example 2-18. Using the Timer control
<%@ Page Language="C#" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack) {
           Label1.Text = DateTime.Now.ToString();
        }
    }

    protected void Timer1_Tick(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Timer Example</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
                <asp:Timer ID="Timer1" runat="server" OnTick="Timer1_Tick"
                 Interval="10000">
                </asp:Timer>
            </ContentTemplate>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

In this case, there are only three visible controls on the page. The first is the ScriptManager control followed by a Label and the Timer control. When this page loads for the first time, the Label control is populated with the DateTime value through the invocation of the Page_Load event handler. After this initial load of the DateTime value to the Label control, the Timer control takes care of changing this value.

The OnTick attribute from the Timer control enables you to accomplish this task. It points to the event that is triggered when the time span specified in the Interval attribute is reached.

The Interval attribute is set to 10000, which is 10,000 milliseconds (remember that there are 1,000 milliseconds to every second). This means, that every 10 seconds an asynchronous postback is performed and the Timer1_Tick() function is called.

When you run this page, the time changes on the page every 10 seconds.

2.3.5. The UpdateProgress Control

The final server control in the AJAX Extensions section of Visual Studio 2008 is the UpdateProgress control. Some asynchronous postbacks take time to execute because of the size of the response or because of the computing time required to get a result together to send back to the client. The UpdateProgress control allows you to provide a visual signifier to the clients to show that indeed work is being done and they will get results soon (and that the browser simply didn't just lock up).

Listing 2-19 shows a textual implementation of the UpdateProgress control.

Example 2-19. Using the UpdateProgess control to show a text message to the client
<%@ Page Language="C#" %>

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e)
   {
        System.Threading.Thread.Sleep(10000);
        Label1.Text = "This button was clicked on " + DateTime.Now.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>UpdatePanel</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:UpdateProgress ID="UpdateProgress1" runat="server">
            <ProgressTemplate>
                An update is occurring...
            </ProgressTemplate>
        </asp:UpdateProgress>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                <asp:Label ID="Label1" runat="server"></asp:Label>
            </ContentTemplate>
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
            </Triggers>
        </asp:UpdatePanel>
        <br />
        <br />
        <asp:Button ID="Button1" runat="server"
         Text="Click to initiate async request"
         OnClick="Button1_Click" />
    </div>
    </form>
</body>
</html>

To add some delay to the response (in order to simulate a long-running computer process) the Thread.Sleep() method is called. From here, you add an UpdateProgess control to the part of the page where you want the update message to be presented. In this case, the UpdateProgress control was added above the UpdatePanel server control. This control does not go inside the UpdatePanel control; instead, it sits outside of the control. However, like the UpdatePanel control, the UpdateProgress control is a template control.

The UpdateProgress control has only a single sub-element: the <ProgressTemplate> element. Whatever you place in this section of the control will appear when the UpdateProgress control is triggered. In this case, the only item present in this section of the control is some text. When you run this page, you get the update shown in Figure 2-12.

The text will appear immediately in this case and will not disappear until the asynchronous postback has finished. The items you put in the <ProgressTemplate> section is actually contained in the page, but its display is turned off through Cascading Style Sheets (CSS).

<div id="UpdateProgress1" style="display:none;">
   An update is occurring...
</div>

Figure 2-12. Figure 2-12

2.3.5.1. Controlling When the Message Appears

Right now, the UpdateProgress appears as soon as the button is clicked. However, some of your processes might not take that long and you might not always want a progress notification going out to the client. The UpdateProgress control includes a DisplayAfter attribute, which allows you to control when the progress update message appears. The use of the DisplayAfter attribute is shown in Listing 2-20.

Example 2-20. Using the DisplayAfter attribute
<asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="5000">
   <ProgressTemplate>
      An update is occurring...
   </ProgressTemplate>
</asp:UpdateProgress>

The value of the DisplayAfter property is a number that represents the number of milliseconds that the UpdateProgress control will wait until it displays what is contained within the <ProgressTemplate> section. The code in Listing 2-20 specifies that the text found in the <ProgressTemplate> section will not be displayed for 5,000 milliseconds (5 seconds).

2.3.5.2. Adding an Image to the <ProcessTemplate>

The previous examples, which make use of the UpdateProgress control, use this control with text, but you can put anything you want within this template control. For instance, you can put a spinning wheel image that will show the end user that the request is being processed. The use of the image is shown in Listing 2-21.

Example 2-21. Using an image in the <ProcessTemplate> section
<asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="5000">
   <ProgressTemplate>
      <asp:Image ID="Image1" runat="server" ImageUrl="~/spinningwheel.gif" />
   </ProgressTemplate>
</asp:UpdateProgress>

Just as the in the text approach, the code for the image is already placed on the client's page instance and is just turned off via CSS:

<div id="UpdateProgress1" style="display:none;">
   <img id="Image1" src="spinningwheel.gif" style="border-width:0px;" />
</div>

2.3.5.3. Displaying Behaviors of the Progress Indicators

The DynamicLayout property of the UpdateProgress control is one that you might not change that often. The default behavior of this property is not to allocate any space for the contents of the UpdateProgress control. Figure 2-13 presents a page when no update is being made.

Figure 2-13. Figure 2-13

Notice that no space is provided for the Progress.gif image. Instead, room is made for this indicator only when it is needed. One result of this setting is that the rest of the page is shifted down slightly to make room for the indicators that you end up using. This can be disconcerting to users to see the page layout change and shift for a short time period. To preallocate the space required for the progress indicator and avoid a shift in the layout, you will want to set the DynamicLayout property to false as presented here:

<asp:UpdateProgress ID="UpdateProgress1" runat="server" DynamicLayout="false"  >

This produces a page with the results illustrated in Figure 2-14.

The layout options really depend on the nature of your application. One point to understand is that the UpdateProgress control will display its indicator for every asynchronous postback that occurs on the page unless you assign it specifically to a single UpdatePanel, using the AssociatedUpdatePanelID property:

<asp:UpdateProgress ID="UpdateProgress1" runat="server"
AssociatedUpdatePanelID="UpdatePanel1" >

Figure 2-14. Figure 2-14

One common approach is to use a CSS class with the UpdateProgress control that manipulates its positioning so that the <ProgressTemplate> contents are displayed as a layer right over the top of the UpdatePanel being affected. The following is an example set of styles that, when associated with a <div> element for an UpdateProgress, produces a small square on the screen to contain whatever images or other information you want to display while an update is occurring.

<style type="text/css">
    .progress
    {
        border:solid;
        background-color: white;
        position: absolute;
        top: 180px;
        left: 180px;
        width: 60px;
        height: 60px;
        padding-left:40px;
        padding-top:40px;
        z-index:1
    }
    </style>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset