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.
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:
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 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.
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.
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.
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:
<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:
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:
<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:
<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 span
s 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:
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:
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:
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:
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) {
var doc = xr.responseXML;
var qty;
if (doc.evaluate) {
qty = doc.evaluate("//text()", doc,
null,
XPathResult.STRING_TYPE, null).stringValue;
}
else {
qty = doc.selectSingleNode("//text()").data;
}
itemQuantityDisplay.innerHTML = qty + " in stock";
itemQuantityDisplay.className = "";
} else {
itemQuantityDisplay.innerHTML = "Error retrieving quantity";
itemQuantityDisplay.className = "Error";
}
itemQuantityDisplay.style.display = "";
progressDisplay.style.display = "none";
}
}
xr.send(null);
progressDisplay.style.display = "";
itemQuantityDisplay.style.display = "none";
}
We can see the four steps involved in using the XMLHttpRequest
object:
- Instantiate the
XMLHttpRequest
object.
- 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.
- 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.
- 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.
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:
- 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.
- 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.
- 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
:
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:
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:
- The first parameter is an instance of the class which implements
ICallbackEventHandler
. In our example, this will be the Page
itself.
- 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.
- 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
.
- 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.
- The fifth parameter is the name of the JavaScript function which gets called when an error occurs during the callback process.
- 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:
function onCallbackComplete(result, context){
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()
{
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.
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.
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.
<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:
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";
WarehouseService.GetItemQuantity(itemList.value,
onCallbackComplete,
onCallbackError,
onCallbackError);
}
Sys.Runtime.load.add(function() {
itemList = document.getElementById("ItemList");
itemQuantityDisplay = document.getElementById("ItemQuantityDisplay");
progressDisplay = document.getElementById("ProgressDisplay");
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.
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:
[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:
WarehouseService.GetItemQuantity(itemList.value,
onCallbackComplete,
onCallbackError,
onCallbackError);
This is how you will make a call to a page method:
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.
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
:
<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:
- The
ContentTemplate
contains the ASP.NET markup for server controls and the HTML which needs to be dynamically updated.
- 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
:
<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:
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 UpdatePanel
s 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.
<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.
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, 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:
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:
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:
<%@ 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 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.
<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.
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.
<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.
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.