WHAT’S IN THIS CHAPTER?
WROX.COM CODE DOWNLOADS FOR THIS CHAPTER
Please note that all the code examples in this chapter are available as a part of this chapter’s code download on the book’s website at www.wrox.com on the Download Code tab.
In web application development, Ajax signifies the capability to build applications that make use of the XMLHttpRequest object, an object responsible for making HTTP requests.
The creation and the inclusion of the XMLHttpRequest object in JavaScript and the fact that all modern browsers support it led to the creation of the Ajax model. Ajax applications initially gained popularity after Google released a number of notable, Ajax-enabled applications, such as Google Maps. These and other applications demonstrated the value of Ajax and were noticed by the world at large. The original inventor of XMLHttpRequest was actually Microsoft; they implemented this functionality as an ActiveX component for Internet Explorer, and all other browsers ported it to their browsers. Nowadays, Internet Explorer supports XMLHttpRequest as a native JavaScript object (as all other browsers do).
Shortly thereafter, Microsoft released a beta for a toolkit that enabled developers to incorporate Ajax features in their web applications. This toolkit, code-named Atlas and later renamed ASP.NET AJAX, makes using Ajax features in your applications extremely simple.
If you are using ASP.NET 3.5 or 4 or 4.5, then you don’t even have to worry about installing the ASP.NET AJAX framework because everything you need is already in place for you.
ASP.NET AJAX makes your web applications seem more fluid. Ajax-enabled applications are responsive and give the end user immediate feedback and direction through the workflows that you provide.
So, what is Ajax doing to your web application? Take a look at what a web page does when it does not use Ajax. Figure 23-1 shows a typical request-and-response activity for a web application.
In this figure, an end user makes a request from his browser to the application that is stored on your web server. The server processes the request and ASP.NET renders a page, which is then sent to the requestor as a response. The response, when received by the end user, is displayed within the end user’s browser.
From here, many events that take place within the application instance as it sits within the end user’s browser cause the complete request-and-response process to reoccur. For instance, the end user might click a radio button, a check box, a button, a calendar, or anything else, and this action causes the entire web page to be refreshed or a new page to be provided.
In contrast, an Ajax-enabled web page includes a JavaScript library on the client that takes care of issuing requests to the web server. It does this when it is possible to send a request and get a response for just part of the page and when using script; the client library updates that part of the page without updating the entire page. An entire page requires a lot of bandwidth to be sent down to the browser to process each and every time. By processing just part of the page, the end user experiences what some people term “fluidity” in the page, which makes the page seem more responsive. The amount of code of the portion of a page that is updated is less and produces the responsiveness the end user expects. Figure 23-2 shows a diagram of how this works.
Figure 23-2 demonstrates that the first thing that happens is that the entire page is delivered in the initial request and response. From there, any partial updates required by the page are done using the client script library. This library can make asynchronous page requests and update just the portion of the page that needs updating. One major advantage to this is that a minimal amount of data is transferred for the updates to occur. Updating a partial page is better than recalling the entire page for what is just a small change to the page.
Ajax is dependent on a few technologies for it to work. The first is the XMLHttpRequest object. This object allows the browser to communicate to a backend server and has been available in the Microsoft world since Internet Explorer 5 through the MSXML ActiveX component. Of course, the other major component is JavaScript. This technology provides the client-side initiation to communication with the backend services and takes care of packaging a message to send to any server-side services. Another aspect of Ajax is support for the Document Object Model (DOM). These pieces change the page when the asynchronous response is received from the server. Finally, the last piece is the data that is being transferred from the client to the server. This transfer is done in XML, or more importantly in the effective JavaScript Object Notation (JSON).
Support for the XMLHttpRequest object gives JavaScript functions within the client script library the capability to call server-side code. As stated, HTTP requests are typically issued by a browser. The browser also takes care of processing the response that comes from the server and then usually regenerates the entire web page in the browser after a response is issued. Figure 23-3 details this process.
If you use the XMLHttpRequest object from your JavaScript library, then you do not actually issue full-page requests from the browser. Instead, you use a client-side script engine (which is basically a JavaScript function) to initiate the request and also to receive the response. Because you are also not issuing a request and response to deal with the entire web page, you can skip a lot of the page processing because it is not needed. This is the essence of an Ajax web request. It is illustrated in Figure 23-4.
As stated, Ajax opens the door to a tremendous number of possibilities. Microsoft has provided the necessary script engines to automate much of the communication that must take place for Ajax-style functionality to occur.
Not only is ASP.NET AJAX a part of the Visual Studio 2012 IDE, but the ASP.NET AJAX product is also baked into the .NET Framework 4.5. This means that to use ASP.NET AJAX, you don’t need to install anything if you are working with ASP.NET 4.5.
Overall, Microsoft has fully integrated the entire ASP.NET AJAX experience so you can easily use Visual Studio and its visual designers to work with your Ajax-enabled pages and even have the full debugging story that you would want to have with your applications. Using Visual Studio 2012, you can also debug the JavaScript that you are using in the pages.
In addition, it is important to note that Microsoft focused a lot of attention on cross-platform compatibility with ASP.NET AJAX. You will find that the Ajax-enabled applications that you build on the .NET Framework 4.5 can work within all the major browsers out there (such as Firefox and Chrome).
The ASP.NET AJAX story really has two parts. The first is a client-side framework and a set of services that are completely on the client-side. The other part of the story is a server-side framework. Remember that the client-side of ASP.NET AJAX is all about the client communicating asynchronous requests to the server-side of the offering.
For this reason, Microsoft offers a Client Script Library, which is a JavaScript library that takes care of the required communications. Figure 23-5 shows the Client Script Library.
The Client Script Library provides a JavaScript, object-oriented interface that is reasonably consistent with aspects of the .NET Framework. Because browser compatibility components are built in, any work that you build in this layer or (in most cases) work that you let ASP.NET AJAX perform for you here will function with a multitude of different browsers. Also, several components support a rich UI infrastructure that produces many things that would take some serious time to build yourself.
The interesting thing about the client-side technologies that are provided by ASP.NET AJAX is that they are completely independent of ASP.NET. In fact, any developer can freely download the Microsoft AJAX Library (again from asp.net/ajax) and use it with other web technologies such as PHP (php.net) and Java Server Pages (JSP) — even though admittedly almost no one does that. With that said, the entire web story is a lot more complete with the server-side technologies that are provided with ASP.NET AJAX.
And even though Microsoft now embraces the jQuery JavaScript library (which is covered in Chapter 25), there are some unique and convenient features exclusive to ASP.NET AJAX.
As an ASP.NET developer, you will likely spend most of your time on the server-side aspect of ASP.NET AJAX. Remember that ASP.NET AJAX is all about the client-side technologies talking back to the server-side technologies. You can actually perform quite a bit on the server-side of ASP.NET AJAX.
The server-side framework knows how to deal with client requests (such as putting responses in the correct format). The server-side framework also takes care of the marshalling of objects back and forth between JavaScript objects and the .NET objects that you are using in your server-side code. Figure 23-6 illustrates the server-side framework provided by ASP.NET AJAX.
When you have the .NET Framework 4.5, you will have the ASP.NET AJAX Server Extensions on top of the core ASP.NET Framework, the Windows Communication Foundation, as well as ASP.NET-based web services (.asmx).
A couple of types of web developers exist out there. There are the web developers who are accustomed to working with ASP.NET and who have experience working with server-side controls and manipulating these controls on the server-side. Then there are developers who concentrate on the client-side and work with the DOM and JavaScript to manipulate and control the page and its behaviors (and then there are those who fit in both types, of course).
With that said, it is important to realize that ASP.NET AJAX was designed for both types of developers. If you want to work more on the server-side of ASP.NET AJAX, then you can use the ScriptManager control and the UpdatePanel control to Ajax-enable your current ASP.NET applications with little work on your part. You can do all this work using the same programming models that you are quite familiar with in ASP.NET.
In turn, you can also use the Client Script Library directly and gain greater control over what is happening on the client’s machine. Next, this chapter looks at building a simple web application that makes use of Ajax.
Now that you understand the reasons for Ajax, the next step is to build a basic sample using this framework. Create a new empty ASP.NET website application using the New Web Site dialog box (as shown in Figure 23-7) and choose an arbitrary name.
After you create the application, you are presented with what is now a standard website project. If you were building this application with ASP.NET 3.5, you would have noticed some additional settings in the web.config file. In this case, at the top of the web.config file, you would find some configuration sections that are registered that deal with AJAX in particular. In the case of ASP.NET 4.5, this same section of configuration previously found in the web.config file is now found back in the machine.config file. Listing 23-1 presents this section from the machine.config.
LISTING 23-1: The <configSections> element found in the machine.config
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>
<!-- Configuration removed for clarity -->
</configuration>
Next, you build a simple ASP.NET page that does not yet make use of Ajax.
To build a simple page that does not yet make use of the Ajax capabilities offered by ASP.NET 4.5, your page needs only a Label control and Button server control. Listing 23-2 presents the code for the page.
LISTING 23-2: A simple ASP.NET 4.5 page that does not use Ajax
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Label1.Text = DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>My Normal ASP.NET Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server"></asp:Label>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Click to get machine time"
onclick="Button1_Click" />
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = DateTime.Now.ToString();
}
</script>
When you open this page in the browser, it contains only a single button. When the button is clicked, the Label control that is on the page is populated with the time from the server machine. Before the button is clicked, the page’s markup is similar to the code presented in Listing 23-3.
LISTING 23-3: The page output for a page that is not using Ajax
<!DOCTYPE html>
<html>
<head><title>
My Normal ASP.NET Page
</title></head>
<body>
<form method="post" action="Listing 23-02.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="pVEIMzKuHfHM8A10duc6kbGylGmyb0H8CjA0LI9dKlVRyylcUJXhQRd/p3cvsGTRor7wtWsUZGgu8uEWGnFcx3/tLVfqhgqN8aAP4Jxb9M0=" />
</div>
<div class="aspNetHidden">
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/QH9EPcAvi4a4BLc182HKfMOBKUpxA+2dZkl9KS33lgGgRBza5bYURjDLLkyCwA6OKzqthVHfjDFy9aW9il4V4EYWRlN974paiCVSFbfEa8jprjN5bAl2MbXFMtIVBZu" />
</div>
<div>
<span id="Label1"></span>
<br />
<br />
<input type="submit" name="Button1" value="Click to get machine time" id="Button1" />
</div>
</form>
</body>
</html>
There is not much in this code. There is a little ViewState and a typical form that will be posted back to the Default.aspx page. When the end user clicks the button on the page, a full postback to the server occurs, and the entire page is reprocessed and returned to the client’s browser. Really, the only change made to the page is that the <span id="Lable1"></span> element is populated with a value, but in this case, the entire page is returned.
The next step is to build on the page from Listing 23-2 and add Ajax capabilities to it. In this example, you add some additional controls. Two of the controls are typical ASP.NET server controls — another Label and Button server control. In addition to these controls, you must add some ASP.NET AJAX controls.
In the Visual Studio 2012 toolbox is a section entitled AJAX Extensions. This section is shown in Figure 23-8.
From AJAX Extensions, add a ScriptManager server control to the top of the page and include the second Label and Button control inside the UpdatePanel control. The UpdatePanel control is a template server control and allows you to include any number of items within it (just as other templated ASP.NET server controls). When you have your page set up, it should look something like Figure 23-9.
Listing 23-4 shows the code for this page.
LISTING 23-4: A simple ASP.NET AJAX page
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Label1.Text = DateTime.Now.ToString()
End Sub
Protected Sub Button2_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Label2.Text = DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>My ASP.NET AJAX Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:Label ID="Label1" runat="server"></asp:Label>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Click to get machine time"
onclick="Button1_Click" />
<br />
<br />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Label2" runat="server" Text=""></asp:Label>
<br />
<br />
<asp:Button ID="Button2" runat="server"
Text="Click to get machine time using Ajax"
onclick="Button2_Click" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = DateTime.Now.ToString();
}
protected void Button2_Click(object sender, EventArgs e)
{
Label2.Text = DateTime.Now.ToString();
}
</script>
When you open this page in the browser, it has two buttons. The first button causes a complete page postback and updates the current time in the Label1 server control. Clicking on the second button causes a partial Ajax asynchronous postback. Clicking this second button updates the current server time in the Label2 server control. When you click the Ajax button, the time in Label1 will not change at all, as it is outside of the UpdatePanel. Figure 23-10 presents a screenshot of the final result.
When you first open the page from Listing 23-4, the code of the page is quite different from the page that you built without using Ajax. Listing 23-5 shows the page results that you will see.
LISTING 23-5: The page output for a page that is using Ajax
<!DOCTYPE html>
<html>
<head><title>
My ASP.NET AJAX Page
</title></head>
<body>
<form method="post" action="Listing 23-04.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="WWtnNoYlMaNb9VaXbk6Zu8WKDrp6d2htzRxkrRGD7jPvrZP2WPLX0FA7YUBF4pYZRlQogz6EK+PuFjiYhwMDKAFkqBFCct1EC2uWNZzT+xI=" />
</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="/WebResource.axd?d=pynGkmcFUV13He1Qd6_TZOfsGAliiRzhbNCK-_Bbdm5qc7-I-DHw9-fyqR7RqDfDXC3Wksp3fzYjv78lBXTZtA2&t=634776625276709129" type="text/javascript"></script>
<script src="/ScriptResource.axd?d=D9drwtSJ4hBA6O8UhT6CQhrg7DRKGxEuvpwpedj7vopaTC0bITXA9Dx80QJ3ecSfbXi4t2bNWufm4ED2tyMQPnb6IIgfUv2Z17WvQn25Nc6r2Il_j0Z1DvnIupc15FHAgUL6JsUsSt_CcKQNorkPXUgaBAhSYXnodIyqbwTUy3Y1&t=6119e399" 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="/ScriptResource.axd?d=JnUc-DEDOM5KzzVKtsL1tbPwOqzhED1dD8elz4bygtpKPKhJiqq0b_O9X56JcIGvkszfugIvdyvz3Efx8suOaifmIwTH_7frKsQOuAjd6Fm3WaKZGEdw75nIgO80ypkfh2PCNd7QinNN1Xyd7uh6dROIOZ6HqaAT27wpkGD-lfYDELDKBMimdCxNgTRLQi6M0&t=6119e399" type="text/javascript"></script>
<div class="aspNetHidden">
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="RGmI1vtxaurgSA6tbndg3zXfgDOK9D+pWkEkUg4X/kJbGksDfFNL5ML2hEEetD+00phBDkOCzf97bxcGQcog/Bk/jsv574AGkbqT26VM8KsxUvIFRSJWeA3b6vs/4gGwQMU4fPn2SlR1v5kfVEwOdQ==" />
</div>
<div>
<script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', 'form1', ['tUpdatePanel1','UpdatePanel1'], [], [], 90, '');
//]]>
</script>
<span id="Label1"></span>
<br />
<br />
<input type="submit" name="Button1" value="Click to get machine time" id="Button1" />
<br />
<br />
<div id="UpdatePanel1">
<span id="Label2"></span>
<br />
<br />
<input type="submit" name="Button2" value="Click to get machine time using Ajax" id="Button2" />
</div>
</div>
</form>
</body>
</html>
From there, if you click Button1 and perform the full-page postback, you get this entire bit of code back in a response — even though you are interested in updating only a small portion of the page! However, if you click Button2 — the Ajax button — you send the request shown in Listing 23-6.
LISTING 23-6: The asynchronous request from the ASP.NET AJAX page
POST /Listing%2023-04.aspx HTTP/1.1
Accept: */*
X-Requested-With: XMLHttpRequest
X-MicrosoftAjax: Delta=true
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Referer: http://localhost:1315/Listing%2023-04.aspx
Accept-Language: en-US,en;q=0.8,de-DE;q=0.5,de;q=0.3
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: localhost:1315
Content-Length: 457
DNT: 1
Connection: Keep-Alive
ScriptManager1=UpdatePanel1%7CButton2&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=AGUaegmKl6ukZ0igyXSeqIUW1YM%2F56cB9CvQ%2BKmbx6wzbEtmJZ3b5wehOayVnxS1fia6%2BEH9ZF98axT8I7yvNNWGS5qzl%2BlxWxTFNqFTb%2FSb0lYWeXwW31q%2BnqvTOxzO12aGjtZOv9oKuWW2qFU%2F%2B7J%2B4e2HDAkJTKdlH6dlVAc%3D&__EVENTVALIDATION=sKpjfJEGzCQ6ObwXMOi1u%2BSAz2Of341uW4OCIOfTPbUgYxWzz9G6nJLAh1r3NzY7Fa4vcU0p4Ft783W%2BP5KzZGu4HkiUROxRR1trx3hE%2FtkFy%2B%2B9%2Bx0lmRQsnaDK%2FljYNW52e438i4wQ5SyqOAadWA%3D%3D&__ASYNCPOST=true&Button2=Click%20to%20get%20machine%20time%20using%20AJAX
Listing 23-7 shows the response for this asynchronous request.
LISTING 23-7: The asynchronous response from the ASP.NET AJAX page
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcQ2hyaXN0aWFuXERvY3VtZW50c1xWaXN1YWwgU3R1ZGlvIDIwMTJcV2ViU2l0ZXNcV3JveFxMaXN0aW5nIDE4LTA0LmFzcHg=?=
X-Powered-By: ASP.NET
Date: Fri, 07 Dec 2012 21:09:56 GMT
1|#||4|240|updatePanel|UpdatePanel1|
<span id="Label2">12/7/2012 10:09:56 PM</span>
<br />
<br />
<input type="submit" name="Button2" value="Click to get machine time using Ajax" id="Button2" />
|172|hiddenField|__VIEWSTATE|zhb9R4rHc1avl1CCq/gAoOeF7XPxtQaoiv64pBVojEhaVevAbRJq6e9rDJe5fC0thgxo51Ls55qg42/6uEUys1ld1hlzM0t0JmLv55CIhsyD5meWk6kxHkYO4X0jxdIVj8YbphKp+CB485z/cPAzZk1LkQs7J96ZtzLGfg11cv4=|152|hiddenField|__EVENTVALIDATION|sCJzmPa+sVbkZ2zvON/kvEn6/7qjGZKpheMuCF34am8ZaaHtdk+Zdepz+uf97h+RZWWAv5/is+Mwig6DEPEOOI+oAwOMqfPwnUOF1Dt3G5L/6KwE3EPyK1XLmd9Xm/29BKYhM0RGxwO3lH2/wuBRXQ==|0|asyncPostBackControlIDs|||0|postBackControlIDs|||26|updatePanelIDs||tUpdatePanel1,UpdatePanel1|0|childUpdatePanelIDs|||25|panelsToRefreshIDs||UpdatePanel1,UpdatePanel1|2|asyncPostBackTimeout||90|18|formAction||Listing 23-04.aspx|20|pageTitle||My ASP.NET AJAX Page|
From Listing 23-7, you can see that the response is much smaller than an entire web page! In fact, the main part of the response is only the code that is contained within the UpdatePanel server control and nothing more. The items at the bottom of the output deal with the ViewState of the page (as it has now changed) and some other small page changes.
When you look at the AJAX Extensions section in the Visual Studio 2012 toolbox, you will notice that not many controls are at your disposal. The controls 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 Ajax model, then look at the Ajax Control Toolkit — a separate download that is covered in Chapter 24.
Table 23-1 describes the ASP.NET AJAX server controls that come with ASP.NET 4.5.
ASP.NET AJAX SERVER CONTROL | DESCRIPTION |
ScriptManager | A component control that manages the marshalling of messages to the Ajax-enabled server for the parts of the page requiring partial updates. Every ASP.NET page requires a ScriptManager control in order to work. Note that you can have only a single ScriptManager control on a page. |
ScriptManagerProxy | A component control that acts as a ScriptManager control for a content page. The ScriptManagerProxy control, which sits on the content page (or sub-page), works in conjunction with a required ScriptManager control that resides on the master page. |
Timer | The Timer control executes client-side events at specific intervals and allows specific parts of your page to update or refresh at these moments. |
UpdatePanel | A 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. |
UpdateProgress | A 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 control is ideal to use when you have long-running Ajax updates. |
The next few sections of this chapter look at these controls and explain how to use them within your ASP.NET pages.
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 for which you want to use the Ajax capabilities provided by ASP.NET 4.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 used on your page as well as marshalling the messages back and forth between the server and the client for the partial page rendering process. The marshalling of the messages can be done using either SOAP or JSON through the ScriptManager control.
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. Listing 23-8 presents the page for this task.
LISTING 23-8: An ASP.NET page that includes only the ScriptManager control
<%@ Page Language="C#" %>
<!DOCTYPE html>
<html>
<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 23-8, 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. Listing 23-9 shows the page output from this bit of ASP.NET code.
LISTING 23-9: The page output from the ScriptManager control
<!DOCTYPE html>
<html>
<head><title>
The ScriptManager Control
</title></head>
<body>
<form method="post" action="Listing 23-08.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="zpV5crkAwVuhffQapeLOpcyY7E3sDEmepTrY/bsvgCn/drFkzuIEIbGMZSnm8fkgNjzDGQ29rapFR3M/2jUcMfMh6z291dYhyQUzLL0wLQA=" />
</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="/WebResource.axd?d=pynGkmcFUV13He1Qd6_TZOfsGAliiRzhbNCK-_Bbdm5qc7-I-DHw9-fyqR7RqDfDXC3Wksp3fzYjv78lBXTZtA2&t=634776625276709129" type="text/javascript"></script>
<script src="/ScriptResource.axd?d=D9drwtSJ4hBA6O8UhT6CQhrg7DRKGxEuvpwpedj7vopaTC0bITXA9Dx80QJ3ecSfbXi4t2bNWufm4ED2tyMQPnb6IIgfUv2Z17WvQn25Nc6r2Il_j0Z1DvnIupc15FHAgUL6JsUsSt_CcKQNorkPXUgaBAhSYXnodIyqbwTUy3Y1&t=6119e399" 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="/ScriptResource.axd?d=JnUc-DEDOM5KzzVKtsL1tbPwOqzhED1dD8elz4bygtpKPKhJiqq0b_O9X56JcIGvkszfugIvdyvz3Efx8suOaifmIwTH_7frKsQOuAjd6Fm3WaKZGEdw75nIgO80ypkfh2PCNd7QinNN1Xyd7uh6dROIOZ6HqaAT27wpkGD-lfYDELDKBMimdCxNgTRLQi6M0&t=6119e399" type="text/javascript"></script>
<div>
<script type="text/javascript">
//<![CDATA[
Sys.WebForms.PageRequestManager._initialize('ScriptManager1', 'form1', [], [], [], 90, '');
//]]>
</script>
</div>
</form>
</body>
</html>
The page output shows that a number of JavaScript libraries load with the page. Also notice that the scripts’ sources are dynamically registered and available through the HTTP handler provided through the ScriptResource.axd handler.
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.
The ScriptManagerProxy control was actually introduced in Chapter 16, as this control deals specifically with master pages and user controls. As with the ScriptManager control covered 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 using 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 box, you simply just select the typical master page option and just change the code a bit in order to deal with Ajax. This is demonstrated in Listing 23-10.
LISTING 23-10: The Ajax master page
<%@ Master Language="C#" %>
<script runat="server">
</script>
<!DOCTYPE html>
<html>
<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, indeed, a ScriptManager control is on the page and that this page will be added to each and every content page that uses this master page. You do not need 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 want to modify the ScriptManager control that is on the master page in any way, then you must add a ScriptManagerProxy control to the content page, as shown in Listing 23-11.
LISTING 23-11: 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 a ScriptManager control does not happen to be on the master page, you will get an error.
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 in time. To accomplish this, you use the Timer control available 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 timed intervals, as illustrated in Listing 23-12.
LISTING 23-12: Using the Timer control
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If Not Page.IsPostBack Then
Label1.Text = DateTime.Now.ToString()
End If
End Sub
Protected Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs)
Label1.Text = DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head id="Head1" 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>
C#
<%@ 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>
In this case, only three controls are 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 function 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, you see the time change on the page every 10 seconds.
The UpdatePanel server control is an Ajax-specific control that is part of ASP.NET 4.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 is a way to trigger a partial page postback and update only the portion of the page that the UpdatePanel specifies.
You have 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 23-13.
LISTING 23-13: Putting the triggers inside of the UpdatePanel control
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Label1.Text = "This button was clicked on " & DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head id="Head1" 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>
C#
<%@ 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>
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 <Triggers>. 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 23-13, that 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.
Listing 23-13 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 also sending back the entire code for the button that is on the page — as presented here:
1|#||4|282|updatePanel|UpdatePanel1|
<span id="Label1">This button was clicked on 12/7/2012 10:15:13 PM</span>
<br />
<br />
<input type="submit" name="Button1" value="Click to initiate async request"
id="Button1" />
|216|hiddenField|__VIEWSTATE|9SRD65c4+HKABtyXj1cwTSdqRx5djmaw4UM/
pnsbaV6BW9Yp3Jt0dMEUO49qM6bpcgVY7ZGaizibE3WuKeC39UPCgR3ivwhh+47AYvrBWPoKY7YpNww7zr7mo5HoMqgY
JeUnJIPlXQml2/gFiOl8CmEbk/9u0t156Bjjw/rEWOYJdNSXHBxsWJgGe2cA+JZstvkNnOaJtGBjlOHfkkhqCg==|128
|hiddenField|__EVENTVALIDATION|rcyhqzLnEyzijEQwniWhKYVo9K47PWymKxRO+QMdU2BwNSArt0P8hu3
3J7bgc+80rIB2IUeWXrR3YLsPZyDoFl2BoHyJ7gCCmjzhPUyUrSH4c0xR3c9EjVqL2BlZQYvK|0|asyncPostBackCont
rol
IDs|||0|postBackControlIDs|||26|updatePanelIDs||tUpdatePanel1,UpdatePanel1|0|childUpdatePanel
IDs
|||25|panelsToRefreshIDs||UpdatePanel1,UpdatePanel1|2|asyncPostBackTimeout||90|18|formAction|
|Listing 23-13.aspx|19|pageTitle||UpdatePanel Control|
This bit of code 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 must include a <Triggers> section within the control.
The reason for this is that although the content that you want to change with the asynchronous postback is all contained within the <ContentTemplate> section, you need 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 23-13, as shown in Listing 23-14.
LISTING 23-14: Using a trigger to cause the asynchronous page postback
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Label1.Text = "This button was clicked on " & DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head id="Head1" 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>
C#
<%@ 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>
In this case, the Button control and the HTML elements are outside of 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 will cause a full-page postback, whereas the AsyncPostBackTrigger control will cause only an asynchronous page postback (obviously, as described by the names of the controls).
As you can see from the example in Listing 23-14, 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 here (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:
1|#||4|113|updatePanel|UpdatePanel1|
<span id="Label1">This button was clicked on 12/7/2012 10:16:00 PM</span>
|216|hiddenField|__VIEWSTATE|exYy/K/aPNML3q+zrqHMWbTkzeycwcLnuu
HX4mfKnCfPJggjY8k7//liUwxhyUEg2eJU/QLD4Cgr4E/B/V7ossp0Rcn2zO5s17I/0oK2mw0MKM0/
OQ3F2NtqeCR5rwic9FiqbVx8G8V52uW0CL1pdnLArj9pi/NSyYpeeVmEAXapYQklsDPvcSKpHmjhIWIhfJ0QmSvO6Yv7LUS
k4OFETg==|128|hiddenField|__EVENTVALIDATION|MdQFKILTdqEbZgfpPoEQwHNM/A1icFRG9iE9Qy+b6wB4LQZQXWf
e9OK1Y6nNzLoFtTFX/hOdRHxv1Fci3EZNZSfpap+bu5DHqP+wDA6qL85imfWkquq8you1y59ztNKU|15|asyncPostBackC
ontrolIDs||Button1,Button1|0|postBackControlIDs|||26|updatePanelIDs||tUpdatePanel1,UpdatePanel1
|0|childUpdatePanelIDs|||25|panelsToRefreshIDs||UpdatePanel1,UpdatePanel1|2|asyncPostBackTimeou
t||90|18|formAction||Listing 23-14.aspx|11|pageTitle||UpdatePanel|
Although not considerably smaller than the previous example, it is, in fact, just a bit smaller — 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.
If you like to work on the design surface of Visual Studio when building your ASP.NET pages, then 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 in the Properties dialog box 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, as illustrated in Figure 23-11.
Clicking on this button in the Properties dialog box launches the UpdatePanelTrigger Collection Editor, as shown in Figure 23-12. This editor allows you to add any number of triggers and to associate them to a control and a control event very easily.
Clicking OK adds the trigger to the <Triggers> section of your UpdatePanel control.
The final server control in the AJAX Extensions section of Visual Studio 2012 is the UpdateProgress control. Some asynchronous postbacks take some 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 that they will get results soon (and that the browser didn’t just lock up).
Listing 23-15 shows a textual implementation of the UpdateProgress control.
LISTING 23-15: Using the UpdateProgress control to show a text message to the client
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
System.Threading.Thread.Sleep(10000)
Label1.Text = "This button was clicked on " & DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head id="Head1" 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>
C#
<%@ 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>
To add some delay to the response (to simulate a long-running computer process), the Thread.Sleep() method is called. From here, you add an UpdateProgress 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, the update shown in Figure 23-13 appears.
The text appears immediately in this case and will not disappear until the asynchronous postback has finished. The code you put in the <ProgressTemplate> section is actually contained in the page, but its display is turned off through CSS.
<div id="UpdateProgress1" style="display:none;">
An update is occurring. . .
</div>
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. Listing 23-16 shows the use of the DisplayAfter attribute.
LISTING 23-16: 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 23-16 specifies that the text found in the <ProgressTemplate> section will not be displayed for 5,000 milliseconds (5 seconds).
The previous examples, which use 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. Listing 23-17 shows the use of the image.
LISTING 23-17: 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 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>
So far, this chapter has showed you how to work with a single UpdatePanel control, but it is important to realize that you can have multiple UpdatePanel controls on a single page. This, in the end, gives you the ability to control the output to specific regions of the page when you want.
Listing 23-18 presents an example of using more than a single UpdatePanel control.
LISTING 23-18: Using more than one UpdatePanel control
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Label1.Text = "Label1 was populated on " & DateTime.Now.ToString()
Label2.Text = "Label2 was populated on " & DateTime.Now.ToString()
End Sub
</script>
<!DOCTYPE html>
<html>
<head id="Head1" runat="server">
<title>Multiple UpdatePanel Controls</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>
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<asp:Label ID="Label2" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
<br />
<br />
<asp:Button ID="Button1" runat="server"
Text="Click to initiate async request"
OnClick="Button1_Click" />
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "Label1 was populated on " + DateTime.Now;
Label2.Text = "Label2 was populated on " + DateTime.Now;
}
</script>
This page is interesting. It has two UpdatePanel controls: UpdatePanel1 and UpdatePanel2. Both of these controls contain a single Label control that at one point can take a date/time value from a server response.
The UpdatePanel1 control has an associated trigger: the Button control on the page. When this button is clicked, the Button1_Click() event triggers and does its job. If you run this page, both of the UpdatePanel controls are updated according to the Button1_Click() event, as illustrated in Figure 23-14.
Both UpdatePanel sections were updated with the Button1_Click() event because, by default, all UpdatePanel controls on a single page update with each asynchronous postback that occurs. This means that the postback that occurred with the Button1 button control also causes a postback to occur with the UpdatePanel2 control.
You can actually control this behavior through the UpdatePanel’s UpdateMode property. The UpdateMode property can take two possible enumerations — Always and Conditional. If you do not set this property, then it uses the value of Always, meaning that each UpdatePanel control always updates with each asynchronous request.
The other option is to set the property to Conditional. This means that the UpdatePanel updates only if one of the trigger conditions is met. For an example of this, change the UpdatePanel controls on the page so that they are now using an UpdateMode of Conditional, as shown in Listing 23-19.
LISTING 23-19: Using more than one (conditional) UpdatePanel control
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
Label1.Text = "Label1 was populated on " & DateTime.Now.ToString()
Label2.Text = "Label2 was populated on " & DateTime.Now.ToString()
End Sub
</script>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "Label1 was populated on " + DateTime.Now;
Label2.Text = "Label2 was populated on " + DateTime.Now;
}
</script>
<!DOCTYPE html>
<html>
<head id="Head1" runat="server">
<title>Multiple UpdatePanel Controls</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<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>
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="Label2" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
<br />
<br />
<asp:Button ID="Button1" runat="server"
Text="Click to initiate async request"
OnClick="Button1_Click" />
</div>
</form>
</body>
</html>
Now that both of the UpdatePanel controls are set to have an UpdateMode of Conditional, when running this page, you will see the results presented in Figure 23-15.
In this case, only the right Label control, Label1, was updated with the asynchronous request even though the Button1_Click() event tries to change the values of both Label1 and Label2. The reason for this is that the UpdatePanel2 control had no trigger that was met.
One issue when working with any Ajax page is that when the end user clicks the browser’s Back button, it destroys the asynchronous requests that are occurring between the current page and the server and any kind of state that you might be managing between these requests.
If you have a process of working with Ajax in the page and you clicked the Back button within the browser, you would go to the page that was just prior to the Ajax-enabled page, regardless of the asynchronous page requests that occurred prior to this action.
Also, if your first page in navigation is a series of asynchronous page requests, as an end user, you will never see the option to click on the Back button of the page, even if you work your way quite far into the navigation that the page provides.
With ASP.NET AJAX, you have the capability to work with the back history of your application. It isn’t as simple as just activating something; it will take a little bit of coding to accomplish what you want. You need to work through a process of telling the ASP.NET page what state you are going to keep track of as you allow end users to navigate with the Back and Forward buttons on the browser.
To see this scenario in action, create a simple page that uses Ajax. This page is presented in Listing 23-20.
LISTING 23-20: Building a basic ASP.NET AJAX page
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
PopulateFields(TextBox1.Text)
End Sub
Private Sub PopulateFields(ByVal InputName As String)
If InputName Is Nothing Then
Label1.Text = "Hello there. What is your name?"
Else
Label1.Text = "Hello there " & HttpUtility.HtmlEncode(InputName)
End If
End Sub
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Ajax Page</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"
Text="Hello there. What is your name?"></asp:Label><br />
<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
<asp:Button ID="Button1" runat="server" Text="Submit Name"
OnClick="Button1_Click" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
PopulateFields(TextBox1.Text);
}
private void PopulateFields(string InputName)
{
if (InputName == null)
{
Label1.Text = "Hello there. What is your name?";
}
else
{
Label1.Text = "Hello there " + HttpUtility.HtmlEncode(InputName);
}
}
</script>
This is a standard ASP.NET AJAX page, and nothing tricky is going on here. In this case, you use Ajax to enter your name into the textbox, and then this name is put into the Label control that is on the page. However, if you enter more than one name, you will find that it seems as if you are working through multiple pages, but the browser’s navigation buttons (the Back and Forward buttons) are not enabled, and you do not have the option to work back to the previous items you entered into the textbox. Also, browser bookmarks do not work, since they only store the (never changing) URL of the page, not its state.
Unfortunately, end users expect this type of behavior, so the new capability adds the back history to these types of pages.
If you are using ASP.NET 3.5 SP1 or higher and you look at the ScriptManager control that is on the page, you will notice a property called EnableHistory. It is set to False by default, but for this example, you want to set this property to True.
The changes made to the EnableHistory property are not the only changes you must put into place when working with the back history on ASP.NET AJAX pages. You need to instruct ASP.NET on how to remember the previous page and what to do when a user navigates to a previous page (or even a later page if the user has clicked the Back button a few times).
You can specify easily the history points to remember and the indexes to use when you are on the page. Listing 23-21 shows a complete instance of a page that remembers the state for users working with the Back and Forward buttons on the browser.
LISTING 23-21: Adding history capabilities
VB
<%@ Page Language="VB" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
PopulateFields(TextBox1.Text)
End Sub
Private Sub PopulateFields(ByVal InputName As String)
If InputName Is Nothing Then
Label1.Text = "Hello there. What is your name?"
Else
Label1.Text = "Hello there " & InputName
End If
If ScriptManager1.IsInAsyncPostBack AndAlso _
(Not ScriptManager1.IsNavigating) Then
ScriptManager1.AddHistoryPoint("myIndexPoint", InputName, _
String.Format("Entering name: {0}", InputName))
Else
TextBox1.Text = InputName
Page.Title = String.Format("Entering name: {0}", InputName)
End If
End Sub
Protected Sub ScriptManager1_Navigate(ByVal sender As Object, _
ByVal e As HistoryEventArgs)
PopulateFields(e.State("myIndexPoint"))
End Sub
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Ajax Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnableHistory="True"
OnNavigate="ScriptManager1_Navigate">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Label1" runat="server"
Text="Hello there. What is your name?"></asp:Label><br />
<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
<asp:Button ID="Button1" runat="server" Text="Submit Name"
OnClick="Button1_Click" />
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
PopulateFields(TextBox1.Text);
}
private void PopulateFields(string InputName)
{
if (InputName == null)
{
Label1.Text = "Hello there. What is your name?";
}
else
{
Label1.Text = "Hello there " + InputName;
}
if (ScriptManager1.IsInAsyncPostBack && !ScriptManager1.IsNavigating)
{
ScriptManager1.AddHistoryPoint("myIndexPoint", InputName,
string.Format("Entering name: {0}", InputName));
}
else
{
TextBox1.Text = InputName;
Page.Title = string.Format("Entering name: {0}", InputName);
}
}
protected void ScriptManager1_Navigate(object sender, HistoryEventArgs e)
{
PopulateFields(e.State["myIndexPoint"]);
}
</script>
Now, looking at the event that occurs when the button on the page is clicked, you can see there are some changes to it to deal with the registration of the history point:
if (ScriptManager1.IsInAsyncPostBack && !ScriptManager1.IsNavigating)
{
ScriptManager1.AddHistoryPoint("myIndexPoint", InputName,
string.Format("Entering name: {0}", InputName));
}
else
{
TextBox1.Text = InputName;
Page.Title = string.Format("Entering name: {0}", InputName);
}
A check occurs to see whether the ScriptManager control on the page is performing an asynchronous postback or is not navigating. If either of these is the case, then a history point is registered using the AddHistoryPoint() event. The idea of the AddHistoryPoint() call is that you are able to add a key/value pair to define the state of the index that you want the page to remember. In this case, the key is the string myIndexPoint and the value is what was provided from the Textbox server control that is on the page. The last option in the list of input parameters is the title to use for the page. This page title will appear in the browser on the page tab (if you have page tabs) as well as in the list of navigation items in the list of option items for the Back and Forward buttons.
In addition to calling AddHistoryPoint() with a single key/value pair, you can also pass in an entire set of them using the NameValueCollection object.
So, in the case of this example, the key myIndexPoint is used along with the value of what was placed in the TextBox1 server control, and this name is also used in the page title.
After you have this code in place, you must also create a Navigate() method off the ScriptManager control that instructs what should be done when one of the buttons is used. You use this method to provide the index of the item that you are working with:
protected void ScriptManager1_Navigate(object sender, HistoryEventArgs e)
{
PopulateFields(e.State["myIndexPoint"]);
}
Here, you can see that HistoryEventArgs provides you with the access to the items that you have registered.
Running this page now, sequentially entering in names, and clicking on the page’s button control provides you with a history of items in the Back button list of options. You are able also to navigate back to this item (or forward, if that’s what is needed), and you will be returned to the page that you were working with. This list from the Back button is illustrated in Figure 23-16.
From the URL on the page, you are able to see how the state is stored. In this case, you end up with something similar to the following:
http://localhost:1315/Listing%2023-21.aspx#&&wqMi/gEg0K2EhqXXhZ/
AnMrZ5icHgZTHIkB2OLoKnWV/+5utJPEUUtMfqp9c3dYDB89CnH5SlUPmDYfcy2Q9V91S5tAKv9ulfKHf2h30dkw=
Notice that the index point is encrypted in the URL. This is the default behavior, although, as with most things in ASP.NET, you can change this behavior. To change it, from the ScriptManager control, you set the EnableSecureHistoryState property to False.
<asp:ScriptManager ID="ScriptManager1" runat="server" EnableHistory="True"
OnNavigate="ScriptManager1_Navigate" EnableSecureHistoryState="False">
With this code in place, you get a URL something like the following:
http://localhost:1315/Listing%2023-21.aspx#&&myIndexPoint=John+Adams
By default, ASP.NET AJAX pages sometimes download a number of different scripts for the page that is being viewed. When they are being downloaded separately, the performance is worse overall for the page.
The reason for this is that the browser must make a separate request for each of the scripts on the page. This means that the time to get all the scripts is longer than if it were done in one larger batch. Also, calling the scripts separately means that the overall load to the page that is being delivered is larger than it would otherwise be.
ASP.NET 4.5 includes the capability to combine the scripts into a single request and response. This is termed script combining or bundling. This feature is covered in more depth in Chapter 28.
Apart from that, you can specify to ASP.NET the scripts that you want combined via the ScriptManager server control on your page. The trick is figuring out what scripts need to be combined, as it isn’t always that apparent.
For this reason, there is a CodePlex project on the Internet that provides you with a server control that you can place on the page to help you figure this out. The server control, ScriptReferenceProfiler, provides you with a list of the scripts that are required for the page. You can find this control at the following address:
http://aspnet.codeplex.com/releases/view/13356
The project hasn’t been updated in a few years and you will get an error message when you click on one of the links shown in Figure 23-17, but apart from that, the project is still very convenient for determining the scripts needed for script combining with ASP.NET AJAX.
This project comes as a simple .dll file. Right-click within the toolbox of Visual Studio and select Choose Items from the provided menu. A dialog box of new controls that you can add to the toolbox then appears. Click the Browse button and navigate to the .dll file that you just downloaded. After selecting it, click OK to add it to your collection of controls. After this is done, you are ready to create an example page that will be used to combine the scripts for better overall performance. Listing 23-22 presents this new page.
LISTING 23-22: Using the ScriptReferenceProfiler control
VB
<%@ Page Language="VB" %>
<%@ Register Assembly="ScriptReferenceProfiler"
Namespace="ScriptReferenceProfiler" TagPrefix="cc1" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<script runat="server">
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
Label1.Text = "Hello " & TextBox1.Text & ".<br />" & "Today is " & TextBox2.Text
End Sub
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Script Combining</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
What is your name?<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<br />
<br />
What is today's date?<br />
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:CalendarExtender ID="TextBox2_CalendarExtender" runat="server"
Enabled="True" TargetControlID="TextBox2">
</asp:CalendarExtender>
<br />
<br />
<asp:Button ID="Button1" runat="server" Text="Submit"
OnClick="Button1_Click" />
<br />
<br />
<asp:Label ID="Label1" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
<cc1:ScriptReferenceProfiler ID="ScriptReferenceProfiler1" runat="server" />
</div>
</form>
</body>
</html>
C#
<%@ Page Language="C#" %>
<%@ Register Assembly="ScriptReferenceProfiler"
Namespace="ScriptReferenceProfiler" TagPrefix="cc1" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<script runat="server">
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = "Hello " + TextBox1.Text + ".<br />"
+ "Today is " + TextBox2.Text;
}
</script>
This simple page uses Ajax to update a Label server control on the page based on some end user input into your page. The two textbox controls that are on the page ask for the end user’s name and the current date. Adding the ScriptReferenceProfiler control will add a couple of @Register page directives at the top of the page.
Running the page, you see the following results, as illustrated in Figure 23-17.
This page provides you with a list of scripts that are being called for on the page. You are now able to use the ScriptManager control on the page and specify that these are scripts that you are interested in loading with the script-combining feature of ASP.NET.
To accomplish this task, copy the provided configuration script from the page in the browser and paste this text into your ScriptManager control within the <CompositeScript> section (see Listing 23-23).
LISTING 23-23: Combining scripts using the ScriptManager server control
<%@ Page Language="C#" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<script runat="server">
. . .
</script>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Script Combining</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
<CompositeScript>
<Scripts>
<asp:ScriptReference name="MicrosoftAjax.js"/>
<asp:ScriptReference name="MicrosoftAjaxWebForms.js"/>
<asp:ScriptReference name="Common.Common.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="Common.DateTime.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="Compat.Timer.Timer.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="Animation.Animations.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="ExtenderBase.BaseScripts.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="Animation.AnimationBehavior.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="PopupExtender.PopupBehavior.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="Common.Threading.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
<asp:ScriptReference name="Calendar.CalendarBehavior.js"
assembly="AjaxControlToolkit, Version=4.1.60919.0, Culture=neutral, PublicKeyToken=28f01b0e84b6
d53e"/>
</Scripts>
</CompositeScript>
</asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
What is your name?<br />
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<br /><br />
What is today's date?<br />
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:CalendarExtender ID="TextBox2_CalendarExtender" runat="server"
Enabled="True" TargetControlID="TextBox2">
</asp:CalendarExtender>
<br /><br />
<asp:Button ID="Button1" runat="server" Text="Submit"
OnClick="Button1_Click" />
<br /><br />
<asp:Label ID="Label1" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Having all of these defined scripts in the <CompositeScript> section will now signify that all these scripts are to be combined and sent to the page collectively, thereby improving the overall performance of your ASP.NET application.
Ajax has fundamentally changed the way web application development is approached. ASP.NET AJAX is a fine framework that facilitates several Ajax features within the context of an ASP.NET site, including partial page updates. You do not need to completely tear down a page and rebuild it for each and every request. Instead, you are able to rebuild the pages slowly in sections as the end user requests them. Note, however, that Microsoft will further support ASP.NET AJAX, but probably not add any new features to it.
This chapter explored the core foundation of ASP.NET AJAX that is available with the default install of Visual Studio 2012. Beyond that, Ajax offers much more, including the Ajax Control Toolkit, the focus of the next chapter.