Click here to Skip to main content
12,358,863 members (62,954 online)
Click here to Skip to main content
Articles » Web Development » Ajax » General » Downloads

Stats

459.6K views
2.5K downloads
487 bookmarked
Posted

An Introduction to AJAX Techniques and Frameworks for ASP.NET

, 24 Aug 2006 CPOL
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.
IntroAjaxASPNET
article
IntroAjaxASPNET
IntroAjaxASPNET1.JPG
source
IntroAjaxASPNET
Sample
App_Code
vssver2.scc
App_Data
Items.mdf
Items_Log.ldf
vssver2.scc
App_Themes
Default
vssver2.scc
Bin
AjaxPro.dll
Anthem.dll
MagicAjax.dll
Microsoft.Web.Atlas.dll
vssver2.scc
eula.rtf
images
loading.gif
vssver2.scc
ScriptLibrary
vssver2.scc
vssver2.scc
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<title>Untitled 1</title>
</head>
<body>
	<h1>
		An overview of Ajax Programming in ASP.NET</h1>
	<h2>
		Contents</h2>
	<ul>
		<li><a href="#Introduction">Introduction</a></li>
		<li><a href="#The_Example_Web_Page">The Example Web Page</a></li><li>
		<a href="#The_Classical_ASP.NET_Implementation">The Classical ASP.NET Implementation</a></li><li>
		<a href="#Using_ASP.NET_Callbacks_for_Ajax">Using
			ASP.NET Callbacks for Ajax</a></li>
		<li><a href="#Ajax_meets_Atlas">Ajax meets Atlas </a> </li>
		<ul>
			<li><a href="#Using_Web_Services_from_JavaScript">Using Web Services 
			from JavaScript</a></li>
			<li><a href="#Invoking_Page_Methods_from_JavaScript">Invoking Page 
			Methods</a></li>
			<li><a href="#Using_the_UpdatePanel_Server_Control">Using the 
			UpdatePanel Server Control</a></li>
		</ul>
	</ul>
	<ul>
		<li><a href="#Other_Ajax_Frameworks_for_ASP.NET">Other ASP.NET Ajax Frameworks
			</a>
			<ul>
				<li><a href="#Ajax.NET">Ajax.Net</a></li>
				<li><a href="#Anthem.Net">Anthem.Net</a></li>
				<li><a href="#Magic_Ajax">MagicAjax.Net</a></li>
			</ul>
		</li>
	</ul>
	<ul>
		<li><a href="#Summary">Summary</a></li>
	</ul>
	<h2>
		I<a name="Introduction"></a>ntroduction</h2>
	<p>
		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: <a href="http://adaptivepath.com/publications/essays/archives/000385.php">
			Ajax: A New Approach to Web Applications</a>. 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&#39;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&#39;s start by looking at the details of the example.</p>
	<h2>
		<a name="The_Example_Web_Page">The Example Web Page</a></h2>
	<p>
		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, 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 “<a href="http://www.lipsum.com">Lorem Ipsum</a>”
		text. Here is a screenshot of the example web page.</p>
	<img src="IntroAjaxASPNET/IntroAjaxASPNET1.JPG" alt="Screenshot" />
	<p>
		The example uses a SQL Server Express database. You can download SQL Server Express
		freely from the <a href="http://msdn.microsoft.com/vstudio/express/sql/">SQL Server
			web site</a>.&nbsp; The list of items is maintained in a table called <code>Items</code>.
		The table has three columns: <code>ItemID</code>, <code>ItemName </code>and <code>ItemQuantity</code>. The list-box is
		populated by binding it to a <code>SqlDataSource </code>configured with the Items table. The
		<code>ItemID </code>field of the table is bound to a list item value and the <code>ItemName </code>field is
		bound to the display text. The markup of the page is shown below:</p>
	<pre>&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head runat=&quot;server&quot;&gt;
    &lt;title&gt;The Classical ASP.NET Way&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
   &lt;div class=&quot;testContent&quot;&gt;
   	.
   	<strong>Paragraphs of Lorem Ipsum </strong>	.
	&lt;/div&gt;
    &lt;form id=&quot;form1&quot; runat=&quot;server&quot;&gt;
        &lt;div&gt;
            &lt;label for=&quot;ItemList&quot; accesskey=&quot;I&quot;&gt;
                Items:&lt;/label&gt;
            &lt;asp:ListBox runat=&quot;server&quot; ID=&quot;ItemList&quot;				
            DataSourceID=&quot;ItemsSource&quot; DataTextField=&quot;ItemName&quot;
                DataValueField=&quot;ItemID&quot; EnableViewState=&quot;False&quot;&gt;         
            &lt;/asp:ListBox&gt;
            &lt;div id=&quot;ItemQuantityDisplay&quot; runat=&quot;server&quot;&gt;
            <strong>The item quantity will be displayed here </strong>            &lt;/div&gt;
            &lt;asp:SqlDataSource ID=&quot;ItemsSource&quot; runat=&quot;server&quot;
            	ConnectionString=&quot;&lt;%$ ConnectionStrings:Items %&gt;&quot;
                SelectCommand=&quot;SELECT [ItemName], [ItemID] FROM [Items]&quot;&gt;
            &lt;/asp:SqlDataSource&gt;
        &lt;/div&gt;
    &lt;/form&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
	<p>
		Let&#39;s start by looking at how the web page will be coded in classical ASP.NET.</p>
	<h2>
		<a name="The_Classical_ASP.NET_Implementation">The Classical ASP.NET Implementation</a></h2>
	<p>
		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 <code>ItemQuantityDisplay</code>
		<code>div </code>element. The <code>ItemQuantityDisplay </code>has the attribute <code>runat=”server”</code> which allows
		server code to modify the text and other attributes of the <code>div </code>element. We set the
		listbox’s <code>AutoPostback </code>attribute to <code>true </code>so that the form is automatically posted
		back to the server when listbox selection changes.</p>
	<pre lang="cs">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(&quot; {0} in stock&quot;,
             Warehouse.GetItemQuantity(ItemList.SelectedValue));
   } catch (Exception ex) {
      Trace.Write(&quot;GetItemQuantity Error: &quot; + ex.Message);
      ItemQuantityDisplay.InnerText = &quot;Error retrieving quantity&quot;;
      ItemQuantityDisplay.Attributes[&quot;class&quot;] = &quot;Error&quot;;
   }
}
</pre>
	<p>
		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 election. We set the <code>MaintainScrollPositionOnPostBack</code>
		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.</p>
	<p>
		In the <code>ItemList_SelectedIndexChanged </code>we obtain the quantity of items in the Warehouse
		by calling <code>Warehouse.GetItemQuantity</code> method which runs a SQL query to get the item
		quantity. We change the text of the <code>ItemQuantityDisplay </code>by modifying the <code>InnerText</code>
		property, which is set to display the quantity of items in stock when no error occurs.
		If an error occurs, then we modify the CSS class of the <code>ItemQuantityDisplay </code>to “Error”
		(which is defined in a separate style sheet file) and set the text to the user that
		an error occurred.</p>
	<p>
		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 <code>MaintainScrollPositionOnPostback</code>
		tries to make the discontinuous action of postback seem continuous. Now, let’s look
		at the Ajax version of the same application.</p>
	<h2>
		<a name="Continuous_Interaction_with_Ajax">Continuous Interaction with Ajax</a></h2>
	<p>
		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 <code>ItemQuantityDisplay</code>. Since
		the page is not reloaded in its entirety as it was in the classical web application,
		we can expect a smoother interaction.</p>
	<p>
		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 straight forward way
		to do this is to access ASP.NET web service methods through HTTP GET protocol. The
		URL is of the form:</p>
	<pre>http://hostname/path/&lt;webservice&gt;/&lt;methodname&gt;?&lt;parameter name1&gt;=&lt;parameter value1&gt;&amp;&lt;parameter name2&gt;=&lt;parameter value2&gt;
</pre>
	<p>
		You may have to enable the HTTP GET access to ASP.NET web services in the <code>web.config</code>
		file. This can be done by adding configuration entry to web.config as shown in the
		following listing:
	</p>
	<pre>&lt;configuration&gt;
  &lt;system.web&gt;
    &lt;webServices&gt;
      &lt;protocols&gt;
        &lt;add name=&quot;HttpGet&quot;/&gt;
</pre>
	<p>
		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:</p>
	<pre>public class WarehouseService : System.Web.Services.WebService {
    
    [WebMethod]
    public int GetItemQuantity(string itemID) 
    {
        return Warehouse.GetItemQuantity(itemID);
    }
}
</pre>
	<p>
		Assuming that the web service is on your local machine and the item ID whose quantity
		you want to access is <code>79ec4891-a73d-4fcc-ade9-2c2a47f7b2df</code>. You can use the following
		URL:</p>
	<pre>http:://localhost/WareHouseService.asmx/GetItemQuantity?itemID=79ec4891-a73d-4fcc-ade9-2c2a47f7b2df
</pre>
	<p>
		This will return a response of the following form:</p>
	<pre>&lt;int xmlns=”…”&gt;85&lt;/int&gt;</pre>
	<p>
		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 <a href="http://swik.net/Ajax/Ajax+Images">
			http://swik.net/Ajax/Ajax+Images</a>. We select an appropriate image and modify
		the <code>ItemQuantityDisplay </code>as shown in the Listing:
	</p>
	<pre>&lt;div&gt;												
  &lt;span id=&quot;ItemQuantityDisplay&quot;&gt;&lt;/span&gt; 					
  &lt;span id=&quot;ProgressDisplay&quot; style=&quot;display: none&quot;&gt;
      &lt;img src=&quot;images/loading.gif&quot; alt=&quot;Loading&quot; /&gt;
       Checking Stock...
  &lt;/span&gt;
&lt;/div&gt;
</pre>
	<p>
		We now have a <code>div </code>which encloses two spans with ids of <code>ItemQuantityDisplay </code>and <code>ProgressDisplay</code>.
		The <code>ProgressDisplay </code>display span contains an animated GIF image with the text “Checking
		Stock” on its right side. The <code>ProgressDisplay </code>is initially hidden. Displaying a
		feedback while asynchronous operation is in progress in merely a matter of hiding
		the <code>ItemQuantityDisplay </code>span and showing the <code>ProgressDisplay </code>span; when the asynchronous
		operation completes the <code>ProgressDisplay </code>span is hidden and the <code>ItemQuantityDisplay</code>
		<code>span </code>is shown.
	</p>
	<p>
		Next we need to add JavaScript code that will handle selection change events for
		the ItemList and call asynchronously to the server. This can be either an 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
		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.</p>
	<p>
		The <code>XMLHttpRequest </code>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.</p>
	<pre>if (!window.XMLHttpRequest){
   window.XMLHttpRequest = function(){
     return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);
   }
}
</pre>
	<p>
		This code first checks the availability of the <code>XMLHttpRequest </code>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 <code>Microsoft</code>.XMLHTTP and returns
		the object. For simplicity, we ignore those browsers which may not have built-in
		<code>XMLHttpRequest </code>object and which do not support ActiveX Objects. This code allows
		us to instantiate the <code>XMLHttpRequest </code>Object on all browsers as following:
	</p>
	<pre>var xr = new XMLHttpRequest();
</pre>
	<p>
		This works because properties and methods of the window object are considered global.</p>
	<p>
		Now, we need to handle the change event for the ItemList. Mozilla Firefox and other
		W3C DOM Level 2 compatible browser provide a method called <code>addEventListener</code>, where
		as Internet Explorer provides a method called attachEvent. Thus our code to add
		a event handler for the change event will look like:</p>
	<pre>if (itemList.attachEvent) {
  itemList.attachEvent(&quot;onchange&quot;, itemList_OnChange);
}
else {
  itemList.addEventListener(&quot;change&quot;, itemList_OnChange, false);
}
</pre>
	<p>
		The code checks for existence of the <code>attachEvent </code>method and if it exists it calls
		the attachEvent method otherwise it calls <code>addEventListener</code>. 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!</p>
	<p>
		The main work in our application will be done in the <code>itemList_OnChange </code>function,
		shown below:</p>
	<pre>function itemList_OnChange(){
    var xr = new XMLHttpRequest();
    xr.open(&quot;GET&quot;, 
&quot;WarehouseService.asmx/GetItemQuantity?itemID=&quot; + itemList.value, true);
    xr.onreadystatechange = function() {
        if (xr.readyState == 4) {									
            if (xr.status == 200) { <strong>//Successful Request</strong>
                var doc = xr.responseXML;
                var qty;
                
                if (doc.evaluate) {									   
                    qty = doc.evaluate(&quot;//text()&quot;, doc, <strong>//XML Parsing in Mozilla</strong>
                                null,
                                XPathResult.STRING_TYPE, null).stringValue;
                }
                else {
                   qty = doc.selectSingleNode(&quot;//text()&quot;).data;	<strong>//XML Parsing in IE</strong>
                }
                
                itemQuantityDisplay.innerHTML = qty + &quot; in stock&quot;;
                itemQuantityDisplay.className = &quot;&quot;;
            } else {
                itemQuantityDisplay.innerHTML = &quot;Error retrieving quantity&quot;;
                itemQuantityDisplay.className = &quot;Error&quot;;
            }
        
        	<strong>//Hide Progress</strong>
            itemQuantityDisplay.style.display = &quot;&quot;;
            progressDisplay.style.display = &quot;none&quot;;
        }
    }
    xr.send(null);
    
    <strong>//Display progress</strong>
    progressDisplay.style.display = &quot;&quot;;
    itemQuantityDisplay.style.display = &quot;none&quot;;
}
</pre>
	<p>
		We can see the four steps involved in using the <code>XMLHttpRequest </code>object:</p>
	<ol>
		<li>Instantiate the <code>XMLHttpRequest </code>Object. </li>
		<li>Call open method on the <code>XMLHttpRequest </code>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.</li>
		<li>Provide a handler to the <code>onreadystatechange </code>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.</li>
		<li>Call the <code>send </code>method. The <code>send </code>method can take the contents of the request as a
			string. This is useful for posting to the server using HTTP POST.</li>
	</ol>
	<p>
		When the HTTP request ends, either successfully or with errors, the <code>readyState </code>of
		the <code>XMLHttpRequest </code>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 r<code>esponseXML </code>property, which returns
		the response as an XML DOM document (provided the content of response is XML), or
		the <code>responseText </code>property, which returns the response in plain text.</p>
	<p>
		In our case, the response is 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 <code>//text()</code> will work for us. The exact method of using <code>XPath</code>
		is different for Internet Explorer and Mozilla Firefox. In Internet Explorer you
		can use the <code>selectSingleNode </code>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.</p>
	<p>
		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 send, we hide the <code>ItemQuantityDisplay </code>and show the <code>ProgressDisplay</code>
		and when the request is finished, we do the reverse.
	</p>
	<p>
		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 a an animation with the text
		“Checking Stock...” on its right side. The animation disappears after some time
		and 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.</p>
	<p>
		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.</p>
	<h2>
		<a name="Using_ASP.NET_Callbacks_for_Ajax">Using ASP.NET Callbacks for Ajax</a></h2>
	<p>
		ASP.NET 2.0 has a feature called callbacks which allow controls to call server side
		code on a page without a full post back. The call back mechanism is used by the
		<code>TreeView </code>control, the <code>GridView </code>control and the <code>DetailsView </code>control. The <code>TreeView</code>
		control uses callbacks to expand nodes on demand; the <code>GridView </code>uses callbacks for
		sorting and paging; the <code>DetailsView </code>control uses callbacks for paging.
	</p>
	<p>
		The steps involved in using callbacks are following:</p>
	<ol>
		<li>Implement <code>ICallbackEventHandler </code>interface in the control or the <code>Page </code>class. The
			interface has two methods: <code>RaiseCallbackEvent </code>and <code>GetCallbackResult</code>. <code>RaiseCallbackEvent</code>
			takes a string argument which is supplied from the client script invoking the callback;
			the <code>GetCallbackResult </code>returns a string value which is passed to client script.</li>
		<li>Generate the client side script that will invoke the callback. This can be generated
			by calling the <code>GetCallbackEventReference </code>method of <code>ClientScriptManager</code>. An insatnce
			of <code>ClientScriptManager </code>is available from the <code>Page </code>class’s <code>ClientScript </code>property.</li>
		<li>Write code that invokes the client script code generated in step 2. This will probably
			be done in an event handler.</li>
	</ol>
	<p>
		The following listing shows the implementation of <code>ICallbackEventHandler</code></p>
	<pre lang="cs">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(&quot;Error checking quantity&quot;);
   }
 } 
</pre>
	<p>
		The <code>eventArgument </code>parameter of the <code>RaiseCallbackEvent </code>function is passed from the
		client script. In our example this will be the <code>ItemID </code>of the selected item in the
		<code>ItemList </code>listbox. All the <code>RaiseCallbackEvent </code>method implementation needs to do is
		to obtain the item quantity from the <code>eventArgument </code>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 <code>GetCallbackResult </code>method.
	</p>
	<p>
		Implementation of <code>ICallbackEventHandler </code>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 <code>Load </code>event handler of the <code>Page </code>as shown below:</p>
	<pre>protected void Page_Load(object sender, EventArgs e) {
  if (IsCallback)
    return;

  string callBackFunctionCall = 
              ClientScript.GetCallbackEventReference(
              this, 
              &quot;onCallbackComplete&quot;, 								
              &quot;getSelectedItemID()&quot;, 							
              null, 
              &quot;onCallbackError&quot;, 							
              true
	);
      
	Page.ClientScript.RegisterClientScriptBlock(				
			GetType(), 								
			&quot;CallBack&quot;, 							
			&quot;function DoClientCallBack() { &quot; 				
			+ callBackFunctionCall + &quot;} &quot;, 				
			true									
		);
    }
</pre>
	<p>
		Generating client side code is a two step process. First, we call the <code>GetCallbackEventReference</code>
		method. This method takes six parameters:</p>
	<ol>
		<li>The first parameter is an instance of the class which implements <code>ICallbackEventHandler</code>.
			In our example this will be the <code>Page </code>itself.</li>
		<li>The second parameter is the client script function to be called when the callback
			completes. This function is passed the string value returned from the <code>GetCallbackResult</code>.</li>
		<li>The third parameter is the value that needs to be passed to the <code>RaiseCallbackEvent</code>
			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 <code>getSelectedItemID </code>which returns the the selected item value
			in the listbox. We pass the string <code>getSelectedItemID</code>(), which evaluates to the return
			value from the function, in this parameter.</li>
		<li>The fourth parameter is a context that can be associated with the call back instance.
			If there are many callbacks this parameter can be used to distinguish between them.
			This parameter has to be a JavaScript expression.</li>
		<li>The fifth parameter is the name of the JavaScript function which gets called when
			an error occurs during the callback process.</li>
		<li>The final parameter is a <code>Boolean </code>value that indicates whether the callback should
			be synchronous or asynchronous. Since we want to perform an asynchronous operation
			we pass <code>true </code>as the value of this parameter.</li>
	</ol>
	<p>
		The return value from <code>GetCallbackEventReference </code>is a JavaScript expression that
		makes an asynchronous call to the server. We will put this in a separate JavaScript
		function called <code>DoClientCallBack </code>which will in turn be called by the change event
		handler of the <code>ItemList </code>element. We use the <code>ClientScript.RegisterClientScriptBlock
		</code>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.</p>
	<p>
		As usual we need to attach an event handler to handle the change event of the <code>ItemList</code>
		control. The code for this will be exactly same as Listing 1.7. The rest of the
		code is as shown:</p>
	<pre><strong>//Callback success</strong>
function onCallbackComplete(result, context){
  progressDisplay.style.display = &quot;none&quot;;

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

<strong>//Callback error</strong>
function onCallbackError(){
  progressDisplay.style.display = &quot;none&quot;;

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

function itemList_OnChange()		
{
  document.getElementById(&quot;ProgressDisplay&quot;).style.display = &quot;&quot;;
  document.getElementById(&quot;ItemQuantityDisplay&quot;).style.display = &quot;none&quot;;
  DoClientCallBack();
}
</pre>
	<p>
		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 <code>XMLHttpRequest </code>object. The ASP.NET
		callback mechanism takes care of the low level calls; however, we did have to add
		extra code on the server.
	</p>
	<h2>
		<a name="Ajax_meets_Atlas">Ajax meets Atlas</a></h2>
	<p>
		Atlas was built to ease development of Ajax applications. The other great advantage
		of Atlas is that it makes your code compatible across the browsers. You don’t have
		to worry about the differences between the browsers; it is taken care by Atlas most
		of the times. We will see how this is done in the next sections.</p>
	<h3>
		<a name="Using_Web_Services_from_JavaScript">Using Web Services from JavaScript</a></h3>
	<p>
		There are number ways to implement our example we 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 example in one of the preceding sections, our task is greatly simplified.</p>
	<p>
		The first step to use Atlas is to add Atlas script references to the web page. This
		is done by adding a <code>ScriptManager </code>control to the web page where you intend to use
		Atlas. The <code>ScriptManager </code>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 Atlas <code>ScriptManager</code>
		control.</p>
	<pre>&lt;atlas:ScriptManager EnableScriptComponents=&quot;false&quot; ID=&quot;ScriptManager1&quot; runat=&quot;server&quot;&gt;
 &lt;Services&gt;
    &lt;atlas:ServiceReference 
       GenerateProxy=&quot;true&quot; 	
       Path=&quot;WarehouseService.asmx&quot; /&gt;
 &lt;/Services&gt;
 &lt;Scripts&gt;
    &lt;atlas:ScriptReference 
        Path=&quot;ScriptLibrary/AtlasExample.js&quot; /&gt;
 &lt;/Scripts&gt;
&lt;/atlas:ScriptManager&gt;
</pre>
	<p>
		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:.
	</p>
	<pre>var itemList, itemQuantityDisplay, progressDisplay;

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

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

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

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

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

Sys.Runtime.load.add(function() {
    itemList = document.getElementById(&quot;ItemList&quot;);
    itemQuantityDisplay = document.getElementById(&quot;ItemQuantityDisplay&quot;);
    progressDisplay = document.getElementById(&quot;ProgressDisplay&quot;);
    
    <strong>//Attaching the event handler</strong>
    itemList.attachEvent(&quot;onchange&quot;, itemList_OnChange);
});
</pre>
	<p>
		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 construction
		the URL, sending call to the web service and parsing the return values. The details
		of <code>XMLHttpRequest </code>object are completely shielded.</p>
	<p>
		The other thing to notice in the above listing is the use of <code>attachEvent </code>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.
	</p>
	<h3>
		<a name="Invoking_Page_Methods_from_JavaScript">Invoking Page Methods from JavaScript</a></h3>
	<p>
		Another way Atlas enables JavaScript code&nbsp; to call server code is through <code>Page</code>
		methods. You can apply <code>WebMethod </code>attribute to a method in your <code>Page </code>class and this
		method will automatically be available to the JavaScript code. Let&#39;s convert
		our example to use <code>PageMethods</code>.
	</p>
	<p>
		First, we will need to mark a method with the <code>WebMethod </code>attribute:</p>
	<pre>[System.Web.Services.WebMethod]
public int GetItemQuantity() {
   return Warehouse.GetItemQuantity(ItemList.SelectedValue);
}
</pre>
	<p>
		Notice the use of <code>ItemList.SelectedValue</code>. 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 control in the method. The JavaScript code will
		remain almost same as the previous section except for the actual web method invocation.
		This was how the call made in the previous section:
	</p>
	<pre><strong>//Invoking the web service</strong>
WarehouseService.GetItemQuantity(itemList.value,
   onCallbackComplete,  
   onCallbackError, 
   onCallbackError);
</pre>
	<p>
		This is how you will make a call to a page method:</p>
	<pre>PageMethods.GetItemQuantity(onCallbackComplete, onCallbackError, onCallbackError);
</pre>
	<p>
		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 <code>UpdatePanel</code>
		which allows you to develop Ajax application with little or no JavaScript. Let&#39;s
		see how we can use an <code>UpdatePanel </code>for our example.</p>
	<h3>
		<a name="Using_the_UpdatePanel_Server_Control">Using the <code>UpdatePanel </code>Server Control</a></h3>
	<p>
		To use an <code>UpdatePanel </code>you need to place the content which needs to be updated after
		an Ajax call inside the <code>UpdatePanel</code>. As we want the <code>ItemQuantityDisplay </code><code>div </code>to be
		updated, we place it inside the <code>UpdatePanel</code>:</p>
	<pre>&lt;atlas:UpdatePanel ID="ItemQunatityDisplayPanel" Mode="conditional" runat="server"&gt;
   &lt;ContentTemplate&gt;
      &lt;div id="ItemQuantityDisplay" runat="server"&gt;
	  &lt;/div&gt;
   &lt;/ContentTemplate&gt;
   &lt;Triggers&gt;
      &lt;atlas:ControlEventTrigger ControlID="ItemList" EventName="SelectedIndexChanged" /&gt;
   &lt;/Triggers&gt;
&lt;/atlas:UpdatePanel&gt;
</pre>
	<p>
		As we see the <code>UpdatePanel </code>declaration has two parts:</p>
	<ol>
		<li>The <code>ContentTemplate </code>contains the ASP.NET markup for server controls and HTML which
			need to be dynamically updated. </li>
		<li>The <code>Triggers </code>section describes what events actually should cause the <code>UpdatePanel</code>
			contents to be rendered. In the listing above we indicate that if the <code>SelectedIndexChanged</code>
			event is fired on the <code>ItemList </code>control, then the <code>UpdatePanel </code>contents should be
			updated.</li>
	</ol>
	<p>
		You also need to set the property <code>EnablePartialRendering </code>of the <code>ScriptManager </code>control
		to true:</p>
	<pre>&lt;atlas:ScriptManager ID="ScriptManager1" runat="server" <strong>EnablePartialRendering="true"</strong>&gt;
</pre>
	<p>
		Setting <code>EnablePartialRendering </code>to true enables the <code>ScriptManager </code>to control the
		renedering of the page as described later. The server side C# code is listed below:</p>
	<pre>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";
    }
}
</pre>
	<p>
		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 it&#39;s
		behavior is similar to the other Ajax examples and you have not written a single
		line of JavaScript. So how does it all work?</p>
	<p>
		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 <code>XMLHttpRequest</code>
		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 than scans through all the <code>UpdatePanel</code>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 <code>UpdatePanel</code>, which is triggered for an update, are actually rendered and instead
		of sending the Page&#39;s response as HTML, it is send as XML. The client side script
		than parses the response XML and extracts and replaces the updated <code>UpdatePanels</code>&#39;s
		contents.
	</p>
	<p>
		To display progress message to the user when an <code>UpdatePanel </code>is being updated, a
		server called <code>UpdateProgress </code>is made available. The contents of the <code>UpdateProgress</code>
		server control are automatically displayed when the client sends update requests
		to web server and hidden once the update is done.</p>
	<pre>&lt;atlas:UpdateProgress ID="ProgressDisplay" runat="server"&gt;
   &lt;ProgressTemplate&gt;
      &lt;img src="images/loading.gif" alt="Loading" /&gt;
       Checking Stock...
    &lt;/ProgressTemplate&gt;
&lt;/atlas:UpdateProgress&gt;
</pre>
	<p>
		Of course there is lot more to the <code>UpdatePanel </code>control and it will require a separate
		article in itself. The <code>UpdatePanel </code>control is the most important feature of Atlas
		and it will be the primary way Atlas will be used. We have some of the featrures
		of Atlas now let's examine few other frameworks.
	</p>
	<h2>
		<a name="Other_Ajax_Frameworks_for_ASP.NET">Other Ajax Frameworks for ASP.NET</a></h2>
	<p>
		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.
	</p>
	<h3>
		<a name="Ajax.NET">Ajax.NET</a></h3>
	<p>
		<a href="http://ajaxpro.info">Ajax.NET</a>, developed by Michael Schwartz, prides
		itself to be the first Ajax library for ASP.NET.&nbsp; To use Ajax.NET you need
		to download the <code>AjaxPro </code>assembly for the <a href="http://www.ajaxpro.info/">AjaxPro.info</a>
		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:</p>
	<pre>void Page_Load(object sender, EventArgs e) {
   AjaxPro.Utility.RegisterTypeForAjax(GetType());
}

[AjaxPro.AjaxMethod]
public int GetItemQuantity(string itemID)  {
  return Warehouse.GetItemQuantity(itemID);
}
</pre>
	<p>
		Here is how you invoke this method from JavaScript:</p>
	<pre>Intro.AjaxNetExample.GetItemQuantity(itemList.value, onCallbackComplete, onCallbackError);
</pre>
	<p>
		<code>Intro.AjaxNetExample</code> is the name of the class of the ASP.NET Page which is specified
		in the <code>@Page </code> declaration:</p>
	<pre>&lt;%@ Page Language="C#" AutoEventWireup="true" ClassName="Intro.AjaxNetExample"  %&gt;
</pre>
	<p>
		Ajax.Net is a lightweight library and it does not support Atlas functionality like
		cross-browser cmpatibility, server side controls etc. However, you can use the many
		of the free JavaScript libraries for other DHTML work.
	</p>
	<h3>
		<a name="Anthem.Net">Anthem.Net</a></h3>
	<p>
		<a href="http://anthem-dot-net.sourceforge.net/">Anthem.Net</a> 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 use Anthem.net we have to replace the regular ASP.NET controls to
		Anthem.Net&#39;s controls.</p>
	<pre>&lt;div&gt;
  &lt;label for="ItemList" accesskey="I"&gt;
    Items:&lt;/label&gt;
  &lt;anthem:ListBox runat="server" ID="ItemList" DataSourceID="ItemsSource" 
       DataTextField="ItemName"
       DataValueField="ItemID" EnableViewState="False" AutoCallBack="True" 
       OnSelectedIndexChanged="ItemList_SelectedIndexChanged"&gt;
  &lt;/anthem:ListBox&gt;
  &lt;anthem:Panel AutoUpdateAfterCallBack="true" runat="server"&gt;
  &lt;div id="ItemQuantityDisplay" runat="server"&gt;
  &lt;/div&gt;
  &lt;/anthem:Panel&gt;
  &lt;asp:SqlDataSource ID="ItemsSource" runat="server" ConnectionString="&lt;%$ ConnectionStrings:Items %&gt;"
    SelectCommand="SELECT [ItemName], [ItemID] FROM [Items]"&gt;&lt;/asp:SqlDataSource&gt;
&lt;/div&gt;
</pre>
	<p>
		So we replaced the listbox with Anthem's listbox. Notice the <code>AutoCallback </code>property
		set to true. This ensures that when listbox selection changes the Anthem framework
		will use <code>XMLHttpRequest </code>to post the contents of the form. This callback works in
		conjunction with the <code>AutoUpdateAfterCallback </code>property of the <code>anthem:panel</code> 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.
	</p>
	<h3>
		<a name="Magic_Ajax">Magic Ajax</a></h3>
	<p>
		The final Ajax framework we are going to look at is the<a href="http://www.magicajax.net/">
			MagicAjax</a> framework. This started as a code project article by <a href="http://www.codeproject.com/script/Articles/list_articles.asp?userid=1826683">
				Argiris Kirtzidis</a>. In terms of use, MagicAjax is very much similar to using
		Atlas <code>UpdatePanel</code>. You need to enclose the controls and markup that you want to
		Update in the MagicAjax&#39;s <code>AjaxPanel </code>class.
	</p>
	<pre>&lt;magicAjax:AjaxPanel runat="server" ID="MagicAjaxPanel"&gt;
&lt;asp:ListBox runat="server" ID="ItemList" DataSourceID="ItemsSource" DataTextField="ItemName"
    DataValueField="ItemID" EnableViewState="False" AutoPostBack="True" OnSelectedIndexChanged="ItemList_SelectedIndexChanged"&gt;
&lt;/asp:ListBox&gt;

&lt;div id="ItemQuantityDisplay" runat="server"&gt;
&lt;/div&gt;
&lt;/magicAjax:AjaxPanel&gt;
</pre>
	<p>
		The server side code is almost same as the postback version of the sample in one
		of the preceding section. MagicAjax does not require any kind of JavaScript.
	</p>
	<h2>
		<a name="Summary">Summary</a></h2>
	<p>
		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 other three frameworks. MagicAjax.NET and Anthem.NET are well suited
		for lightweight and quick Ajax implementations with full server control support.&nbsp;
		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.</p>
<p>
		The source code includes all the implementations which we have 
		discussed. As usual comments are welcome.</p>
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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

Share

About the Author

Rama Krishna Vavilala
Architect
United States United States
No Biography provided

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160621.1 | Last Updated 24 Aug 2006
Article Copyright 2006 by Rama Krishna Vavilala
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid