Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

An Introduction to AJAX Techniques and Frameworks for ASP.NET

4.90/5 (150 votes)
24 Aug 2006CPOL21 min read 1   2.5K  
This article introduces AJAX to ASP.NET developers, implementing an example web page in different ways using ASP.NET Atlas, ASP.NET callbacks, Ajax.Net, Anthem.Net, and MagicAjax.Net.

Contents

Introduction

By now, the chances are high that you have heard of AJAX in the context of web development. The acronym was coined by Jesse James Garret in his article: AJAX: A New Approach to Web Applications. Several frameworks have emerged since then to support AJAX Web development. In this article, we will examine some of the frameworks that are available for ASP.NET programmers. The emphasis of the article will be on Microsoft's ASP.NET Atlas which is becoming the most important framework for AJAX development in ASP.NET. We will examine different frameworks and the benefits of AJAX through an example of a web page in an e-commerce web site. Let's start by looking at the details of the example.

The Example Web Page

We will develop a simple web page that presents the user with a list of items, sold by the e-commerce web site, displayed in a list box. When the user selects an item from the list box, the text below the list box shows the quantity of the selected item in stock at the warehouse. To properly illustrate the benefits of AJAX, the list-box will appear below paragraphs of “Lorem Ipsum” text. Here is a screenshot of the example web page:

Screenshot

The example uses a SQL Server Express database. You can download SQL Server Express freely from the SQL Server web site. The list of items is maintained in a table called Items. The table has three columns: ItemID, ItemName, and ItemQuantity. The list-box is populated by binding it to a SqlDataSource configured with the Items table. The ItemID field of the table is bound to a list item value, and the ItemName field is bound to the display text. The markup of the page is shown below:

HTML
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>The Classical ASP.NET Way</title>
</head>
<body>
   <div class="testContent">
       .
       Paragraphs of Lorem Ipsum     .
    </div>
    <form id="form1" runat="server">
        <div>
            <label for="ItemList" accesskey="I">
                Items:</label>
            <asp:ListBox runat="server" ID="ItemList"                
            DataSourceID="ItemsSource" DataTextField="ItemName"
                DataValueField="ItemID" EnableViewState="False">         
            </asp:ListBox>
            <div id="ItemQuantityDisplay" runat="server">
            The item quantity will be displayed here   </div>
            <asp:SqlDataSource ID="ItemsSource" runat="server"
                ConnectionString="<%$ ConnectionStrings:Items %>"
                SelectCommand="SELECT [ItemName], [ItemID] FROM [Items]">
            </asp:SqlDataSource>
        </div>
    </form>
 </body>
</html>

Let's start by looking at how the web page will be coded in classical ASP.NET.

The Classical ASP.NET Implementation

Our objective is to display the quantity of item in stock when the user selects an item in the listbox. We can do this by changing the text of the ItemQuantityDisplay div element. The ItemQuantityDisplay has the attribute runat="server" which allows server code to modify the text and other attributes of the div element. We set the listbox’s AutoPostback attribute to true so that the form is automatically posted back to the server when the listbox selection changes.

C#
protected void Page_Load(object sender, EventArgs e) {
   this.MaintainScrollPositionOnPostBack = true;
   ItemQuantityDisplay.Visible = false;
}

protected void ItemList_SelectedIndexChanged(object sender, EventArgs e) 
{
   ItemQuantityDisplay.Visible = true;
   try {
      ItemQuantityDisplay.InnerText =
      String.Format(" {0} in stock",
             Warehouse.GetItemQuantity(ItemList.SelectedValue));
   } catch (Exception ex) {
      Trace.Write("GetItemQuantity Error: " + ex.Message);
      ItemQuantityDisplay.InnerText = "Error retrieving quantity";
      ItemQuantityDisplay.Attributes["class"] = "Error";
   }
}

We have paragraphs of text above the listbox so it is likely that the user may have to scroll to reach the listbox and change its selection. We set the MaintainScrollPositionOnPostBack to true so that after the postback completes, the scroll position of the page is at the same location where the user left it; otherwise, the user will have to scroll to get to the text displaying the item quantity.

In the ItemList_SelectedIndexChanged, we obtain the quantity of items in the warehouse by calling the Warehouse.GetItemQuantity method which runs a SQL query to get the item quantity. We change the text of the ItemQuantityDisplay by modifying the InnerText property, which is set to display the quantity of items in stock when no error occurs. If an error occurs, we modify the CSS class of the ItemQuantityDisplay to “Error” (which is defined in a separate style sheet file) and set the text to the user to show that an error occurred.

Now, let’s see what the user experiences when he browses to the web page. Depending on the browser window height, the user may or may not need to scroll to reach the listbox. Once he reaches the listbox and selects an item, he will see a brief flicker in the web page and see the quantity of item in stock in the text below the list box. He will see that the web page scrolls down when the page is reloaded after he selects the item. Such interaction can no where be termed continuous though MaintainScrollPositionOnPostback tries to make the discontinuous action of the postback seem continuous. Now, let’s look at the AJAX version of the same application.

Continuous Interaction with AJAX

The main promise of AJAX is to provide continuous interaction to users when they use a web site. Let’s see how we can achieve that. In the AJAX version of our application, we will obtain the quantity of the selected item in stock by using the XMLHttpRequest object and use client scripting to alter the text of the ItemQuantityDisplay. Since the page is not reloaded in its entirety as it was in the classical web application, we can expect a smoother interaction.

For JavaScript to obtain the quantity of items in stock, we need to provide an end point in our web site where this information can be obtained. We need to have a URL from where the item quantity can be obtained. The most straightforward way to do this is to access the ASP.NET web service methods through the HTTP GET protocol. The URL is of the form:

http://hostname/path/<webservice>/<methodname>?<name1>=<value1>&<name2>=<value2>

You may have to enable the HTTP GET access to ASP.NET web services in the web.config file. This can be done by adding a configuration entry to the web.config, as shown in the following listing:

XML
<configuration>
  <system.web>
    <webServices>
      <protocols>
        <add name="HttpGet"/>

Next, we create a web service that provides a method to obtain the quantity of an item in stock at the warehouse. The code is shown in the following listing:

C#
public class WarehouseService : System.Web.Services.WebService {
    
    [WebMethod]
    public int GetItemQuantity(string itemID) 
    {
        return Warehouse.GetItemQuantity(itemID);
    }
}

Assuming that the web service is on your local machine and the item ID whose quantity you want to access is 79ec4891-a73d-4fcc-ade9-2c2a47f7b2df, you can use the following URL:

http:://localhost/WareHouseService.asmx/GetItemQuantity?
      itemID=79ec4891-a73d-4fcc-ade9-2c2a47f7b2df

This will return a response of the following form:

XML
<int xmlns="…">85</int>

Now we are ready to code the rest of the application. Since, an AJAX enabled web page talks to the server asynchronously using JavaScript, it is essential to provide the user some feedback. Many AJAX web sites show animated GIFs with a text message. Some of the images that are in the public domain can be downloaded from here. We select an appropriate image, and modify the ItemQuantityDisplay as shown in the listing:

HTML
<div>                                                
  <span id="ItemQuantityDisplay"></span>                     
  <span id="ProgressDisplay" style="display: none">
      <img src="images/loading.gif" alt="Loading" />
       Checking Stock...
  </span>
</div>

We now have a div which encloses two spans with IDs of ItemQuantityDisplay and ProgressDisplay. The ProgressDisplay display span contains an animated GIF image, with the text “Checking Stock” on its right side. The ProgressDisplay is initially hidden. Displaying a feedback while an asynchronous operation is in progress is merely a matter of hiding the ItemQuantityDisplay span and showing the ProgressDisplay span; when the asynchronous operation completes, the ProgressDisplay span is hidden and the ItemQuantityDisplay span is shown.

Next, we need to add JavaScript code that will handle the selection change events for the ItemList and call asynchronously to the server. This can be either embedded within the page or in a separate file. A separate file approach is the preferred way in AJAX applications as script code base tends to be big. The script code in a separate file means that the code can be cached by the browser independently of the page, saving some bandwidth. In our example, we put the code in a separate file.

The XMLHttpRequest object is available as a built-in object for Internet Explorer 7.0, Mozilla Firefox, Opera, and Safari. In Internet Explorer 6.0, it is available as an ActiveX Object. The JavaScript code shown in the listing below accounts for this difference:

JavaScript
if (!window.XMLHttpRequest){
   window.XMLHttpRequest = function(){
     return new ActiveXObject("Microsoft.XMLHTTP");
   }
}

This code first checks the availability of the XMLHttpRequest object. If it is not available, then it adds a new property to the window object of type function. The function instantiates a new ActiveX object with prog ID Microsoft.XMLHTTP and returns the object. For simplicity, we ignore those browsers which may not have a built-in XMLHttpRequest object and which do not support ActiveX objects. This code allows us to instantiate the XMLHttpRequest object on all browsers:

JavaScript
var xr = new XMLHttpRequest();

This works because properties and methods of the window object are considered global.

Now, we need to handle the change event for the ItemList. Mozilla Firefox and other W3C DOM Level 2 compatible browsers provide a method called addEventListener, where as Internet Explorer provides a method called attachEvent. Thus our code to add an event handler for the change event will look like:

JavaScript
if (itemList.attachEvent) {
  itemList.attachEvent("onchange", itemList_OnChange);
}
else {
  itemList.addEventListener("change", itemList_OnChange, false);
}

The code checks for the existence of the attachEvent method, and if it exists, it calls the attachEvent method, otherwise it calls addEventListener. Also note the difference in the event name; in Internet Explorer, it is onchange, and in Mozilla FireFox, it is change. Welcome to the world of browser incompatibilities!

The main work in our application will be done in the itemList_OnChange function, shown below:

JavaScript
function itemList_OnChange(){
    var xr = new XMLHttpRequest();
    xr.open("GET", 
"WarehouseService.asmx/GetItemQuantity?itemID=" + itemList.value, true);
    xr.onreadystatechange = function() {
        if (xr.readyState == 4) {                
            if (xr.status == 200) { //Successful Request
                var doc = xr.responseXML;
                var qty;
                
                if (doc.evaluate) {           
                    qty = doc.evaluate("//text()", doc, 
                    //XML Parsing in Mozilla
                                null,
                                XPathResult.STRING_TYPE, null).stringValue;
                }
                else {
                   //XML Parsing in IE
                   qty = doc.selectSingleNode("//text()").data;    
                }
                
                itemQuantityDisplay.innerHTML = qty + " in stock";
                itemQuantityDisplay.className = "";
            } else {
                itemQuantityDisplay.innerHTML = "Error retrieving quantity";
                itemQuantityDisplay.className = "Error";
            }
        
            //Hide Progress
            itemQuantityDisplay.style.display = "";
            progressDisplay.style.display = "none";
        }
    }
    xr.send(null);
    
    //Display progress
    progressDisplay.style.display = "";
    itemQuantityDisplay.style.display = "none";
}

We can see the four steps involved in using the XMLHttpRequest object:

  1. Instantiate the XMLHttpRequest object.
  2. Call the open method on the XMLHttpRequest object. The first parameter is the HTTP method to use, which is either GET or POST. The second parameter is the URL of the resource that you want to access using the object.
  3. Provide a handler to the onreadystatechange event. The handler will be called by the object as its state changes. The bulk of the work is done in this handler. In the listing, we assign an anonymous function to handle this event.
  4. Call the send method. The send method can take the contents of the request as a string. This is useful for posting to the server using HTTP POST.

When the HTTP request ends, either successfully or with errors, the readyState of the XMLHttpRequest object is set to 4, which means complete. You have to figure whether the request was successful or not using the status property which returns one of the HTTP status codes. The status code of 200 means success. Once the request completes, the HTTP response can be obtained from either the responseXML property, which returns the response as an XML DOM document (provided the content of the response is XML), or the responseText property, which returns the response in plain text.

In our case, the response is an XML document, and we need to extract the item quantity from the XML DOM document. Since the quantity of the item is send as text of the root document, the XPATH //text() will work for us. The exact method of using XPath is different for Internet Explorer and Mozilla Firefox. In Internet Explorer, you can use the selectSingleNode function, which is not available in Mozilla where you have to use the evaluate method. Luckily, this is the last piece of code where we have to consider cross-browser compatibility issues.

The other interesting thing we do in the code for the event handler is hiding and displaying the progress by manipulating the display property of the element’s style. When the HTTP request is sent, we hide the ItemQuantityDisplay and show the ProgressDisplay, and when the request is finished, we do the reverse.

Let’s examine the application from the user interaction perspective, and compare it with the classic ASP.NET application in the previous section. When the user scrolls down to the item list and selects an item, he sees an animation with the text “Checking Stock...” on its right side. The animation disappears after some time, and the user sees either the quantity of items or an error message. There is no screen flicker, and the scroll position does not change. This can be described as a continuous interaction.

This continuous interaction did come at a price. The price being the amount of client-side code we had to write. Fortunately, the client-side code can be simplified. The simplification can be done by encapsulating common code in a framework. Luckily, ASP.NET callbacks provide an elementary framework for calling server-side code from client scripts. We examine that in the next section.

Using ASP.NET Callbacks for AJAX

ASP.NET 2.0 has a feature called callbacks which allow controls to call server-side code on a page without a full postback. The callback mechanism is used by the TreeView control, the GridView control, and the DetailsView control. The TreeView control uses callbacks to expand nodes on demand; the GridView uses callbacks for sorting and paging; the DetailsView control uses callbacks for paging.

The steps involved in using callbacks are the following:

  1. Implement the ICallbackEventHandler interface in the control or the Page class. The interface has two methods: RaiseCallbackEvent and GetCallbackResult. RaiseCallbackEvent takes a string argument which is supplied from the client script invoking the callback; the GetCallbackResult returns a string value which is passed to the client script.
  2. Generate the client-side script that will invoke the callback. This can be generated by calling the GetCallbackEventReference method of ClientScriptManager. An instance of ClientScriptManager is available from the Page class’s ClientScript property.
  3. Write code that invokes the client script code generated in step 2. This will probably be done in an event handler.

The following listing shows the implementation of ICallbackEventHandler:

C#
public string GetCallbackResult()
{
   return callbackResult;
}

public void RaiseCallbackEvent(string eventArgument)
{
  try {
     callbackResult = Warehouse.GetItemQuantity(eventArgument).ToString();
  }
  catch (Exception e)
  {
     Trace.Write(e.Message);
     throw new Exception("Error checking quantity");
  }
}

The eventArgument parameter of the RaiseCallbackEvent function is passed from the client script. In our example, this will be the ItemID of the selected item in the ItemList listbox. All the RaiseCallbackEvent method implementation needs to do is to obtain the item quantity from the eventArgument parameter and put it in a string member variable of the Page. We need a member variable to store the result as we will need to return this in the GetCallbackResult method.

Implementation of ICallbackEventHandler is just a part of the story. Next, we need to generate the client-side script which actually invokes the script. This is done in the Load event handler of the Page, as shown below:

C#
protected void Page_Load(object sender, EventArgs e) {
  if (IsCallback)
      return;

  string callBackFunctionCall = 
              ClientScript.GetCallbackEventReference(
              this, 
              "getSelectedItemID()", 
              "onCallbackComplete",         
              null, 
              "onCallbackError",     
              true
    );
      
  Page.ClientScript.RegisterClientScriptBlock(    
            GetType(), 
            "CallBack",     
            "function DoClientCallBack() { "     
            + callBackFunctionCall + "} ",         
            true        
        );
}

Generating client-side code is a two step process. First, we call the GetCallbackEventReference method. This method takes six parameters:

  1. The first parameter is an instance of the class which implements ICallbackEventHandler. In our example, this will be the Page itself.
  2. The second parameter is the value that needs to be passed to the RaiseCallbackEvent method. The parameter should be a JavaScript expression that evaluates to a string. In our example, we need to pass the selected item value in the listbox. To do this, we create a function getSelectedItemID which returns the selected item value in the listbox. We pass the string getSelectedItemID(), which evaluates to the return value from the function, in this parameter.
  3. The third parameter is the client script function to be called when the callback completes. This function is passed the string value returned from the GetCallbackResult.
  4. The fourth parameter is a context that can be associated with the callback instance. If there are many callbacks, this parameter can be used to distinguish between them. This parameter has to be a JavaScript expression.
  5. The fifth parameter is the name of the JavaScript function which gets called when an error occurs during the callback process.
  6. The final parameter is a boolean value that indicates whether the callback should be synchronous or asynchronous. Since we want to perform an asynchronous operation, we pass true as the value of this parameter.

The return value from GetCallbackEventReference is a JavaScript expression that makes an asynchronous call to the server. We will put this in a separate JavaScript function called DoClientCallBack, which will in turn be called by the change event handler of the ItemList element. We use the ClientScript.RegisterClientScriptBlock function to form the JavaScript function and register the inline script block. This completes the work we have to do on the server. Next, we move to the code on the client.

As usual, we need to attach an event handler to handle the change event of the ItemList control. The code is as shown:

JavaScript
//Callback success
function onCallbackComplete(result, context){
  progressDisplay.style.display = "none";

  itemQuantityDisplay.className = "";
  itemQuantityDisplay.style.display = "";
  itemQuantityDisplay.innerHTML = result + " in stock";
}

//Callback error
function onCallbackError(){
  progressDisplay.style.display = "none";

  itemQuantityDisplay.className = "Error";
  itemQuantityDisplay.style.display = "";
  itemQuantityDisplay.innerHTML = "Error retrieving quantity";
}

function itemList_OnChange()        
{
  document.getElementById("ProgressDisplay").style.display = "";
  document.getElementById("ItemQuantityDisplay").style.display = "none";
  DoClientCallBack();
}

The user interaction in this example is the same as that presented in the previous section, but we can see that the client code is greatly simplified as we no longer have any code to parse the XML or to work with the XMLHttpRequest object. The ASP.NET callback mechanism takes care of the low level calls; however, we did have to add extra code on the server.

AJAX meets Atlas

Atlas was built to ease development of AJAX applications. The other great advantage of Atlas is that it makes your code compatible across browsers. You don’t have to worry about the differences between the browsers; it is taken care by Atlas most of the time. We will see how this is done in the next section.

Using Web Services from JavaScript

There are a number ways to implement our example in Atlas. A core feature of Atlas is the ability to invoke web services from JavaScript code. First, we will use this feature of Atlas. Since we already have a web service in place which we developed for an example in one of the preceding sections, our task is greatly simplified.

The first step to use Atlas is to add Atlas script references to the web page. This is done by adding a ScriptManager control to the web page where you intend to use Atlas. The ScriptManager control takes care of generating appropriate script references for Atlas framework scripts. It can also generate script references for web service proxies (which are automatically generated by Atlas) and also your own custom scripts which may depend on Atlas. The following listing demonstrates the use of the Atlas ScriptManager control.

HTML
<atlas:ScriptManager EnableScriptComponents="false" ID="ScriptManager1" 
    runat="server">
 <Services>
    <atlas:ServiceReference 
       GenerateProxy="true"     
       Path="WarehouseService.asmx" />
 </Services>
 <Scripts>
    <atlas:ScriptReference 
        Path="ScriptLibrary/AtlasExample.js" />
 </Scripts>
</atlas:ScriptManager>

The code shown in the listing is all the code you need to add to the web page. The rest of the code, which is in a JavaScript file, is shown below:

JavaScript
var itemList, itemQuantityDisplay, progressDisplay;

function onCallbackComplete(result){
    progressDisplay.style.display = "none";

    itemQuantityDisplay.className = "";
    itemQuantityDisplay.style.display = "";
    itemQuantityDisplay.innerHTML = result + " in stock";
}

function onCallbackError(){
    progressDisplay.style.display = "none";

    itemQuantityDisplay.className = "Error";
    itemQuantityDisplay.style.display = "";
    itemQuantityDisplay.innerHTML = "Error retrieving quantity";
}

function itemList_OnChange(){
    progressDisplay.style.display = "";
    itemQuantityDisplay.style.display = "none";
    
    //Invoking the web service
    WarehouseService.GetItemQuantity(itemList.value,
       onCallbackComplete,  
       onCallbackError, 
       onCallbackError);
}

Sys.Runtime.load.add(function() {
    itemList = document.getElementById("ItemList");
    itemQuantityDisplay = document.getElementById("ItemQuantityDisplay");
    progressDisplay = document.getElementById("ProgressDisplay");
    
    //Attaching the event handler
    itemList.attachEvent("onchange", itemList_OnChange);
});

The major thing to notice here is the ease of invoking the web service. The web service method is invoked as a regular function call in JavaScript. The results are obtained with the same ease. The Atlas framework takes care of the construction of the URL, sending calls to the web service, and parsing the return values. The details of the XMLHttpRequest object are completely shielded.

The other thing to notice in the above listing is the use of the attachEvent function to add an event handler. There is no special consideration for different browsers. The code just works with Mozilla Firefox and Internet Explorer. This is all made possible by the Atlas cross browser compatibility layer.

Invoking Page Methods from JavaScript

Another way Atlas enables JavaScript code to call server code is through Page methods. You can apply the WebMethod attribute to a method in your Page class, and this method will automatically be available to the JavaScript code. Let's convert our example to use Page methods.

First, we will need to mark a method with the WebMethod attribute:

C#
[System.Web.Services.WebMethod]
public int GetItemQuantity() {
   return Warehouse.GetItemQuantity(ItemList.SelectedValue);
}

Notice the use of ItemList.SelectedValue. In a Page method, all the control values can be accessed as they can be accessed in a postback. So use page methods whenever you require values of other server controls in the method. The JavaScript code will remain almost the same as in the previous section except for the actual web method invocation. This was how the call was made in the previous section:

C#
//Invoking the web service
WarehouseService.GetItemQuantity(itemList.value,
   onCallbackComplete,  
   onCallbackError, 
   onCallbackError);

This is how you will make a call to a page method:

C#
PageMethods.GetItemQuantity(onCallbackComplete, 
                           onCallbackError, 
                           onCallbackError);

All the different ways we have considered so far, except for the postback sample, required us to use JavaScript. Atlas provides a server control called UpdatePanel which allows you to develop AJAX applications with little or no JavaScript. Let's see how we can use an UpdatePanel for our example.

Using the UpdatePanel Server Control

To use an UpdatePanel, you need to place the content which needs to be updated after an AJAX call inside the UpdatePanel. As we want the ItemQuantityDisplay div to be updated, we place it inside the UpdatePanel:

HTML
<atlas:UpdatePanel ID="ItemQunatityDisplayPanel" Mode="conditional" 
       runat="server">
   <ContentTemplate>
      <div id="ItemQuantityDisplay" runat="server">
      </div>
   </ContentTemplate>
   <Triggers>
      <atlas:ControlEventTrigger 
           ControlID="ItemList" 
           EventName="SelectedIndexChanged" />
   </Triggers>
</atlas:UpdatePanel>

As we can see, the UpdatePanel declaration has two parts:

  1. The ContentTemplate contains the ASP.NET markup for server controls and the HTML which needs to be dynamically updated.
  2. The Triggers section describes what events actually should cause the UpdatePanel contents to be rendered. In the listing above, we indicate that if the SelectedIndexChanged event is fired on the ItemList control, then the UpdatePanel contents should be updated.

You also need to set the property EnablePartialRendering of the ScriptManager control to true:

HTML
<atlas:ScriptManager ID="ScriptManager1" runat="server" 
       EnablePartialRendering="true">

Setting EnablePartialRendering to true enables the ScriptManager to control the rendering of the page as described later. The server side C# code is listed below:

C#
protected void Page_Load(object sender, EventArgs e) {
    if (!IsPostback)
       ItemQuantityDisplay.Visible = false;
}

protected void ItemList_SelectedIndexChanged(object sender, EventArgs e) {
    ItemQuantityDisplay.Visible = true;

    try {
        ItemQuantityDisplay.InnerText =
              String.Format(" {0} in stock",
             Warehouse.GetItemQuantity(ItemList.SelectedValue));
    } catch (Exception ex) {
        Trace.Write("GetItemQuantity Error: " + ex.Message);
        ItemQuantityDisplay.InnerText = "Error retrieving quantity";
        ItemQuantityDisplay.Attributes["class"] = "Error";
    }
}

As you might have noticed, the code looks like typical ASP.NET server code in response to a form postback. Yet, when you run the application, you will see that its behavior is similar to the other AJAX examples and you have not written a single line of JavaScript. So how does it all work?

What happens is that any kind of client events which cause a postback are intercepted by the Atlas client framework. The form contents are then posted using the XMLHttpRequest object through JavaScript. From the server perspective, it is like a normal postback, but from the client perspective, the form is not submitted as the browser retains the web page state intact. The server side code then scans through all the UpdatePanels on the page, and checks if they need to be rendered or updated on the client side. This is done by checking the trigger. Only the portions of the page that are inside an UpdatePanel, which is triggered for an update, are actually rendered, and instead of sending the page's response as HTML, it is send as XML. The client-side script then parses the response XML and extracts and replaces the updated UpdatePanels's contents.

To display progress message to the user when an UpdatePanel is being updated, a server control called UpdateProgress is made available. The contents of the UpdateProgress server control are automatically displayed when the client sends update requests to the web server, and hidden once the update is done.

HTML
<atlas:UpdateProgress ID="ProgressDisplay" runat="server">
   <ProgressTemplate>
      <img src="images/loading.gif" alt="Loading" />
       Checking Stock...
    </ProgressTemplate>
</atlas:UpdateProgress>

Of course, there is lot more to the UpdatePanel control, and it will require a separate article in itself. The UpdatePanel control is the most important feature of Atlas, and it will be the primary way Atlas will be used. We have seen here some of the features of Atlas, now, let's examine a few other frameworks.

Other AJAX Frameworks for ASP.NET

Before ASP.NET Atlas was developed, there were several open source implementations to provide AJAX support in ASP.NET. Let's quickly scan through three of the popular open source implementations.

Ajax.NET

Ajax.NET, developed by Michael Schwartz, prides itself to be the first AJAX library for ASP.NET. To use Ajax.NET, you need to download the AjaxPro assembly from the AjaxPro.info web site and place it in the bin directory of your web site. The server code resembles that of Atlas when using page methods, only the name of the attribute changes:

C#
void Page_Load(object sender, EventArgs e) {
   AjaxPro.Utility.RegisterTypeForAjax(GetType());
}

[AjaxPro.AjaxMethod]
public int GetItemQuantity(string itemID)  {
  return Warehouse.GetItemQuantity(itemID);
}

Here is how you invoke this method from JavaScript:

C#
Intro.AjaxNetExample.GetItemQuantity(itemList.value, onCallbackComplete, 
                                                     onCallbackError);

Intro.AjaxNetExample is the name of the class of the ASP.NET page which is specified in the @Page declaration:

ASP.NET
<%@ Page Language="C#" AutoEventWireup="true" 
         ClassName="Intro.AjaxNetExample"  %>

Ajax.Net is a lightweight library, and it does not support Atlas functionalities like cross-browser compatibility, server side controls etc. However, you can use many of the free JavaScript libraries for other DHTML work.

Anthem.Net

Anthem.Net was developed by Jason Diamond, and it supports AJAX in ASP.NET through server controls. It has a set of server controls that extend the regular ASP.NET server controls. To enable our application using Anthem.net, we have to replace the regular ASP.NET controls with Anthem.Net's controls.

HTML
<div>
  <label for="ItemList" accesskey="I">
    Items:</label>
  <anthem:ListBox runat="server" ID="ItemList" DataSourceID="ItemsSource" 
       DataTextField="ItemName"
       DataValueField="ItemID" EnableViewState="False" AutoCallBack="True" 
       OnSelectedIndexChanged="ItemList_SelectedIndexChanged">
  </anthem:ListBox>
  <anthem:Panel AutoUpdateAfterCallBack="true" runat="server">
  <div id="ItemQuantityDisplay" runat="server">
  </div>
  </anthem:Panel>
  <asp:SqlDataSource ID="ItemsSource" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Items %>"
      SelectCommand="SELECT [ItemName], [ItemID] FROM [Items]">
   </asp:SqlDataSource>
</div>

So we replaced the listbox with Anthem's listbox. Notice that the AutoCallback property is set to true. This ensures that when the listbox selection changes, the Anthem framework will use XMLHttpRequest to post the contents of the form. This callback works in conjunction with the AutoUpdateAfterCallback property of the anthem:panel control in the page. Anthem framework looks for updated controls, and sends it back to the client which just replaces the HTML of the updated controls.

Magic Ajax

The final AJAX framework we are going to look at is the MagicAjax framework. This started as a CodeProject article by Argiris Kirtzidis. In terms of use, MagicAjax is very much similar to using Atlas UpdatePanel. You need to enclose the controls and markup that you want to update in the MagicAjax's AjaxPanel class.

HTML
<magicAjax:AjaxPanel runat="server" ID="MagicAjaxPanel">
<asp:ListBox runat="server" ID="ItemList" DataSourceID="ItemsSource" 
    DataTextField="ItemName"
    DataValueField="ItemID" EnableViewState="False" AutoPostBack="True" 
    OnSelectedIndexChanged="ItemList_SelectedIndexChanged">
</asp:ListBox>

<div id="ItemQuantityDisplay" runat="server">
</div>
</magicAjax:AjaxPanel>

The server-side code is almost the same as the postback version of the sample in one of the preceding sections. MagicAjax does not require any kind of JavaScript.

Summary

So we have seen different implementations of a sample in different ways using different frameworks. Exactly what framework you need to use depends on the project and the features you desire from a framework. Atlas is being developed by Microsoft, and is the most feature intensive of all the frameworks; however, Atlas is quite heavyweight compared to the other three frameworks. MagicAjax.NET and Anthem.NET are well suited for lightweight and quick AJAX implementations with full server control support. Ajax.NET is good for lightweight remote calls using JavaScript. ASP.NET callbacks are well suited for control authors who want AJAX functionality for a server control.

The source code includes all the implementations which we have discussed. As usual, comments are welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)