Click here to monitor SSC
  • Av rating:
  • Total votes: 89
  • Total comments: 15
Paul Glavich

Asynchronous client script callbacks

10 August 2005

Call them what you will, there’s value in asynchronous client script callbacks

There has been a lot of interest in the web-facing community lately about a new useability feature that goes by a number of different names—XMLHTTP, AJAX, out-of-band requests, and asynchronous client script callbacks, to name a few.

Regardless of the name, this feature provides a way for a standard web page to make calls back to the server, without a traditional page refresh. The user is oblivious to the fact that a server call has occurred, and is not interrupted by it.

An example is best to illustrate this: You are navigating a web page and select a value from a drop-down list. In a typical scenario, your selection signals the web page to call the server, and you experience a delay while the page retrieves information and displays the results. You could not enter or select information until the server executed the call and returned a response to the browser.

Using asynchronous client script callbacks, the name we will use in this article, you select a value from a drop-down list and a server request is made in the background, so there is no delay. When the server call finishes executing in the background, the details are updated on your browser.

This technology is not new, but it requires browser support. Now that a majority of browsers are on board, users can take advantage of the functionality the technology provides.

Google is doing a trial of the technology to enhance its users’ web-searching experience. Results are loaded in the background while Google displays suggestions for users as they type. You can try it for yourself here.

Where does ASP.NET fit?

ASP.NET V1.0 and V1.1 have no explicit support for this technology. Although they provide basic ways to manipulate script blocks, there is no easy way to incorporate more advanced techniques such as asynchronous client script callbacks.

With the upcoming release of ASP.NET V2.0, however, Microsoft focused on useability. This is evident when implementing common tasks and more complex technologies. In fact, ASP.NET V2.0 contains out-of-the-box support for asynchronous client script callbacks and provides a fairly simple way to register the callback methods, invoke them, and handle any associated errors.

Show me the money

Let’s look at a simple example of asynchronous client script callbacks and how they are implemented within ASP.NET V2.0.

The code below shows a simple page with two drop-down lists. (Note: The code can be downloaded using the link at the top of the page.) One list causes a postback to occur that updates the text-description field. The other drop-down list uses asynchronous client script callbacks to perform the same task. The server-side code inserts a two-second delay to simulate the latency that is typically involved when executing a postback operation.

The two text fields at the bottom of the page serve no purpose other than to accept data input. When executing a postback, data can be typed into these fields, but once the postback is complete, the fields are cleared to their previous state. The two text fields can be edited once the client script callback operation is complete, with no loss of data. The text description field is updated with the computed description value. (Note: The code below represents one ASPX page and its associated code beside file.)

Demo 1: Default.aspx (page component)

<% @ Page Language ="C#" AutoEventWireup ="true"
CodeFile ="Default.aspx.cs" Inherits ="_Default" %>
 

 <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">  
 
 <html xmlns ="http://www.w3.org/1999/xhtml" >
<head runat ="server">
 
   <title> Untitled Page </title>
<script type ="text/javascript">
 
     function ClientCallbackFunction(arg, ctx)
{
       document.all.txtDesc.value = arg; 
     }
   </script> 
 </head>


<body>
 
   <form id ="form1" runat ="server">
<div>
 
   Regular Postback <br />
<asp:DropDownList ID ="ddlPostback" runat ="server" AutoPostBack ="True"
OnSelectedIndexChanged ="ddlPostback_SelectedIndexChanged" Width ="117px">
<asp:ListItem Value ="0">- Select One - </asp:ListItem >
<asp:ListItem Value ="1">Item 1 </asp:ListItem>
<asp:ListItem Value ="2">Item 2 </asp:ListItem>
<asp:ListItem Value ="3">Item 3 </asp:ListItem>
<asp:ListItem Value ="4">Item 4 </asp:ListItem>
    </asp:DropDownList>

<br />
<br />
Async Callback <br />



<asp:DropDownList ID ="ddlAsync" runat ="server" Width ="110px"
onclick ="return DoTheCallback(document.all.ddlAsync.value,0);">

    <asp:ListItem Value ="0">- Select One - </ asp : ListItem >
<asp:ListItem Value ="1">Item 1 </asp:ListItem >
<asp:ListItem Value ="2">Item 2 </asp:ListItem >
<asp:ListItem Value ="3">Item 3 </asp:ListItem >
<asp:ListItem Value ="4">Item 4 </asp:ListItem >

   </asp:DropDownList>

<br />
<br />
<br />
Text Description <br />
<asp:TextBox ID ="txtDesc" runat ="server" Width ="246px"></asp:TextBox >
<br />
<br />
<br />
Other fields <br />
1:
<asp:TextBox ID ="TextBox1" runat ="server"></asp:TextBox>&nbsp;&nbsp;
2:
<asp:TextBox ID ="TextBox3" runat ="server"></asp:TextBox>  
 
  </ div >
</ form >

 </ body >
</ html >  

Demo 1: Default.aspx.cs (server-side code beside component)

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _Default : System.Web.UI. Page, ICallbackEventHandler

{

   protected void Page_Load( object sender, EventArgs e)
{


string js = Page.ClientScript.GetCallbackEventReference( this, "arg",
"ClientCallbackFunction", "ctx", true);

System.Text. StringBuilder sb = new System.Text. StringBuilder();
sb.Append( "function DoTheCallback(arg, ctx) {");
sb.Append(js);
sb.Append( "}");
Page.ClientScript.RegisterClientScriptBlock( this.GetType(),
"callbackkey", sb.ToString(), true);
 
   }
protected void ddlPostback_SelectedIndexChanged( object sender, EventArgs e)
{  
      txtDesc.Text = UpdateTextField(ddlPostback.SelectedValue); 
   }

/// <summary>
/// Simple Server side code to execute when an index
/// is changed on the dropdown lists.
/// </summary>
/// <param name="listValue"></param>
/// <returns></returns>
private string UpdateTextField( string listValue)
{
   System.Threading. Thread.Sleep(2000); // cause a delay
string desc = null;
switch (listValue)
{
case "1": 
desc = "I am number 1!";
break;
  case "2": 
     desc = "Number 2 is cool";
break;
   case "3": 
      desc = "Item 3 feels free…";
break;
   case "4": 
      desc = "4 on the floor.";
break;
    default : 
      desc = "";
break;

     }
return desc;
  }

#region ICallbackEventHandler Members

public string RaiseCallbackEvent( string eventArgument)
{
    return UpdateTextField(eventArgument); 
  }
#endregion
}  

Making the magic

Now let’s look at what is required to make the asynchronous client script callback magic occur within ASP.NET 2.0.

The server-side component, which receives the client script callback event, must implement the ICallbackEventHandler interface. This interface has only one method:

string RaiseCallbackEvent(string eventArgument)

This server-side method is called when the client-side asynchronous method is called. A string, typically the ASPX page, is returned to the caller.

A text description is returned and displayed in the text description field. In a typical page implementation, the ASPX page class must implement the ICallbackEventHandler interface as shown below:

public partial class YourASPXPage : System.Web.UI. Page, ICallbackEventHandler
{
    public string RaiseCallbackEvent( string eventArgument)
{

      . . . . . . . . 

Creating the asynchronous callback event

To invoke an asynchronous client script callback, we must first obtain a reference or entry point to the javascript that invokes the asynchronous method. We can then register the event reference in the ASPX page, so it can be called as required by the client-side script.

To do so, we use the GetCallbackEventReference method that is part of the ClientScriptManager class. All the client-side scripting functionality resides in the ClientScriptManager class, including the RegisterClientScriptBlock and RegisterStartupScript methods that existed in the V1 and V1.1 framework. The ClientScriptManager class is also a member of the System.Web.UI.Page class, so any methods of this class can be accessed as shown below:

string js = Page.ClientScript.GetCallbackEventReference(this, "arg",
"ClientCallbackFunction", "ctx", true);

This returns a block of javascript, or event reference, that can be used to trigger the asynchronous client-side callback. The returned javascript looks like this…

"WebForm_DoCallback('__Page',arg,ClientCallbackFunction,ctx,null,true)"

…because the client-side code has no knowledge of this event reference and the invocation method could change in a future version of the framework. So we need to wrap the client-side code in a well-defined, well-named javascript function that the client-side code knows about and for which it provides a well-known signature.

Compare the code below to the example at the beginning of this article:

protected void Page_Load( object sender, EventArgs e)
{
    string js = Page.ClientScript.GetCallbackEventReference
( this, "arg", "ClientCallbackFunction", "ctx", true);
System.Text. StringBuilder sb = new System.Text. StringBuilder();
sb.Append( "function DoTheCallback(arg, ctx) {");
sb.Append(js);
sb.Append( "}");
Page.ClientScript.RegisterClientScriptBlock
( this.GetType(), "callbackkey", sb.ToString(), true);  
  } 

We have obtained the event reference and registered a javascript function called DoTheCallback, which invokes the asynchronous client-side event.

The javascript function takes two parameters, arg and ctx. These arguments obtain the callback event reference. You could use any parameter name, and the returned callback event reference—the javascript block—specifies the use of the parameters as argument/variable names within the client-side callback method to initiate the callback.

It’s important to remember that the parameters arg and ctx must be used as part of the client-side callback function.

Calling/invoking the asynchronous callback event

We have now created the plumbing required to implement the asynchronous client script call on the client. To initiate the process, the client, or browser, makes a call to the DoTheCallback function that was registered in the server-side code above.

This function accepts two parameters: arg, for passing function arguments to the server-side method; and ctx, which is contextual information that will be passed to the client-side callback when the server-side method has executed. (Note: It is not available to the server-side method.) It is useful to indicate in the client-side callback method the context or mode in which the callback might be operating.

The function to invoke the asynchronous client call is a standard javascript call, so it can be made from anywhere within the browser where javascript can be used.

Displaying/processing callback event results

The javascript call in the browser has initiated an asynchronous client-side call that calls the RaiseCallbackEvent method on the server. This method performs some server-side processing, typically using what was passed in from the asynchronous client-side call as input via the arg parameter, and returns a string.

Execution of the asynchronous client-side call can take two paths. If no error occurs in execution on the server via the RaiseCallbackEvent method, client-side execution continues to the javascript function registered in the server-side code in the Page_Load method using this statement:

string js = Page.ClientScript.GetCallbackEventReference(this, "arg",
"ClientCallbackFunction", "ctx", true);

In this example, it is the ClientCallbackFunction function:

  < script type ="text/javascript">

    function ClientCallbackFunction(arg, ctx)
{
document.all.txtDesc.value = arg;
}

  </ script > 

The string value returned from the RaiseCallbackEvent method on the server is passed in via the arg parameter, and the ctx parameter contains contextual information specified via the DoTheCallback method that initiated the asynchronous call—in this example ‘0’. DHTML is used to update the browser display with the text returned from the server.

If an exception in processing occurs on the server and an error callback is registered, the javascript function is called passing in the error details. This is not shown in this example, but to take advantage of this functionality, you alter the initial call to GetCallbackEventReference to add an additional client-side function to register as shown below:

string js = Page.ClientScript.GetCallbackEventReference(this, "arg",
"ClientCallbackFunction", "ctx", "ClientCallbackErrorFunction", true);

We have registered an additional javascript function, ClientCallbackErrorFunction, to act as the recipient client-side method in the event of an error.

On the client, or browser, we simply provide a javascript method such as:

  function RefreshCPUMonitorCallbackError(err)
  {  
    alert("Error processing callback."); 
  }

Any exception that occurs on the server side will generate a call to this function, passing in error details via the err parameter.

It’s not all a pretty picture

Asynchronous client script callbacks are a powerful feature, but they can be limiting. You can only pass a string to the server-side method, for example, and only return a string. This simplistic data exchange can be a source of frustration when more complex data exchange is required.

In addition, multiple controls on a page require asynchronous processing methods. So for asynchronous processing of multiple elements, you must look at custom ways to split the string into multiple elements to serve the requirements of the page.

Although the ASP.NET V2.0 implementation falls short in flexibility, it is provided out of the box, which means many developers will use it by default. Microsoft is now looking at ways to enhance the functionality of the feature.

In contrast, AJAX.NET provides a robust, full-featured implementation for V1.0 and V1.1 of the .NET framework. The free library supports a number of data types within .NET and uses an attribute-based mechanism for multiple asynchronous method support. (See http://ajax.schwarz-interactive.de/csharpsample/default.aspx for the AJAX implementation.)

Conclusion

This article provides an example of asynchronous client script callbacks in ASP.NET V2.0. It has shown the concept and implementation of the technology, but it can be taken further. The GridView control in ASP.NET V2.0 supports asynchronous client script callbacks by default for features such as data paging. This shift in functionality from previous versions of similar controls will be a boon to useability.

Developers don’t need to know anything about the asynchronous client script callback feature to take advantage of it. The technology is moving into mainstream development, which will shape how it is implemented in the future. The details may differ, but the use of asynchronous client-side callbacks—or whatever name you choose to call them—will play a part in many future projects.

Paul Glavich

Author profile:

Paul Glavich (glav@aspalliance.com) is an ASP.NET MVP who works as a solution Architect for Datacom in Australia. He has more than 15 years of industry experience, and was technical architect for one of the world’s first Internet banking solutions using .NET technology. His technical articles can also be seen on community sites such as ASPAlliance.com (www.aspalliance.com/).

Search for other articles by Paul Glavich

Rate this article:   Avg rating: from a total of 89 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: I tried the example
Posted by: Anonymous (not signed in)
Posted on: Thursday, June 29, 2006 at 4:48 PM
Message: the example works, however, is's not really async, since client has to wait for the server callback, the client can't move to other places.

Subject: Files for download
Posted by: Anonymous (not signed in)
Posted on: Wednesday, August 30, 2006 at 4:40 AM
Message: The downloadable example files does not work.
I get several errors when compiling:

1. "onclick" on drop down
2. _Default does not implement GetCallbackresult

etc.

Subject: example not work properly
Posted by: Anonymous (not signed in)
Posted on: Tuesday, September 12, 2006 at 11:03 PM
Message: i try the example but not work properly, you are not implement ICallbackEventHandler correctly

Subject: Example Code Doesn't Compile
Posted by: Anonymous (not signed in)
Posted on: Thursday, September 14, 2006 at 12:56 PM
Message: Thanks for the waist of my time, your example download does not compile.

Subject: Not working
Posted by: Anonymous (not signed in)
Posted on: Wednesday, November 15, 2006 at 3:07 AM
Message: thankx for irritating the user your code does not work

Subject: I tried the example
Posted by: Anonymous (not signed in)
Posted on: Saturday, November 18, 2006 at 4:58 PM
Message: you are not implement ICallbackEventHandler correctly..Apart from this code works very good.

Subject: I triend the example
Posted by: Anonymous (not signed in)
Posted on: Saturday, November 25, 2006 at 4:02 AM
Message: it work but after submit button click it gives
error of Invalid Callback or Postback argument

Subject: Program is not running
Posted by: Anonymous (not signed in)
Posted on: Saturday, January 20, 2007 at 12:30 AM
Message: It gives an error.
I.e.

'_Default' does not implement interface member 'System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent(string)'. '_Default.RaiseCallbackEvent(string)' is either static, not public, or has the wrong return type.

Subject: Code does not work
Posted by: Anonymous (not signed in)
Posted on: Tuesday, January 30, 2007 at 7:11 PM
Message: Your code does not work. Have you looked at any of the previous comments?

Subject: Not working
Posted by: Anonymous (not signed in)
Posted on: Friday, March 23, 2007 at 4:45 AM
Message: Did u ever tried running the program what u have given ?

Subject: try code before testing
Posted by: Anonymous (not signed in)
Posted on: Wednesday, April 04, 2007 at 4:05 AM
Message: the concept maybe good but the example code does not work.

Subject: HI
Posted by: Anonymous (not signed in)
Posted on: Monday, May 21, 2007 at 4:28 AM
Message: it is very good but i could no understand it

Subject: ICallbackEventHandler Implementation
Posted by: Anonymous (not signed in)
Posted on: Tuesday, May 22, 2007 at 9:42 AM
Message: The implementation of the ICallBackEvenHandler changed from its original design in beta. Now we have:

void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{}

and

string ICallbackEventHandler.GetCallbackResult()
{}

All you need to do is declare a string variable in with Page scope, load its value in the RaiseCallbackEvent and reuse it at GetCallbackResult.

Made short:
public partial class blah: Page, IcallBackEventHandler
{

Subject: It didint fit:
Posted by: Anonymous (not signed in)
Posted on: Tuesday, May 22, 2007 at 9:45 AM
Message: public partial class blah: Page, IcallBackEventHandler
{
string _eventArgument = "";

}
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
this._eventArgument = eventArgument;
}
string ICallbackEventHandler.GetCallbackResult()
{
return "\nPassed event argument: " + this.eventArgument;
}

}

and this will send the return of getCallbackResult() to the client script.

I hope it helps.

Fabio

Subject: Please sign in to comment
Posted by: Tony Davis (view profile)
Posted on: Tuesday, June 19, 2007 at 8:12 AM
Message:

Anonymous comments have been disabled on this article due to relentless spamming.

Please do continue to comment -- but you will need to sign in or join in order to do so. It jsut requires a username, email address and password. Simple-talk does not share user details with any third parties, under any circumstances.

Best,

Tony (ed.)

 


 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.