Article Author: Rick Strahl
Introduction
In the first part of this series on ASP.NET script callbacks, I introduced Microsoft’s new script callback technology. In this article I’ll delve a little deeper by looking at multiple callbacks on a single page and showing how you can implement a non-HTML message scheme to send the results from a callback to the browser. Finally, I’ll round out this article by reviewing some of the pros and cons of callbacks and leave you a few thoughts on best practices for your ASP.NET callbacks.
System Requirements
To run the sample code accompanying this article you will need:
- The .NET Framework 2.0 Beta 2 or August CTP to release
- ASP.NET 2.0 Beta 2 or August CTP to release
- MSDE, SQL Server 2000/2005/Express with the Northwind Database
Installing and Running the Sample Code
Microsoft’s implementation of callbacks has evolved quite rapidly in recent months. The sample code for this series is for the August CTP release of .NET . This means that it should be very similar to the situation when .NET 2.0 is finally released and in particular it takes into account very recent changes Microsoft has made to the callback API. In consequence you won’t be able to use it with earlier releases, including beta 2.
The sample code for this article is identical to that for the part 1 article. You can download the code and install it into a directory of your choice. To check out the application start Visual Studio 2005 and open a Web Project from the directory you installed to. Alternately if you don’t use Visual Studio, run the included _WebConfiguration.exe file, which lets you create an IIS virtual directory and set it to a specific ASP.NET version. Make sure you pick the 2.0 version installed on your system (Beta 2 or August CTP or later).
The sample uses the SQL Server Northwind database as well as a set of business objects and a custom, strongly typed configuration settings class. This class is used by the business objects to retrieve connection string information consistently. The configuration class reads and writes its values to the web.config in the AppSettings section, provided the application has permissions to do so. You can modify the ConnectionString key to point at your local Northwind database.
If you prefer to view the samples in action rather than downloading and installing them first, they are online on my own site, at http://www.west-wind.com/presentations/scriptcallbacks/sample/
Multiple Callbacks per Page and non-HTML Results
In the last article we’ve dealt only with a single callback on a page. You’ll find though that once you start using the client callback functionality that it’s very easy to find new uses for it and soon you will end up with multiple callbacks on a single page.
Take a look at Figure 1, which is another variation of Northwind data lookups. On this form, I can select a customer, which automatically invokes a lookup for invoices for that customer; that in turn causes a lookup for lineitems. Both invoice and lineitem lookups are managed through script callbacks.

Figure 1. A page that uses multiple callbacks on a single page
Script callbacks in ASP.NET are implemented using the ICallbackEvent interface, which has two methods, RaiseCallbackEvent() and GetCallbackResult() . In the previous article, I implemented this interface on the form, which works just fine for single callbacks. Things get trickier though, once you have more than one callback. The problem is that ASP.NET gives you only a single callback handler and the handler doesn’t receive any meaningful context information that let’s you identify which control or operation initiated the callback.
You do get to assign an event context, which is the originating control (in this case the form) but if you have more than one callback, there’s still no way with a single implementation to differentiate between the multiple callbacks, unless you pass that information as part of the inbound event argument. That’s very ugly.
Luckily the workaround is not difficult: Rather than implementing the callback event interface on the form, you can create the event interface on a new control. Specifically one control for each callback. You create a control, implement the ICallbackEvent interface on it, and then assign the appropriate event handler to the specific client side control event. Then when the events are fired they are routed to the appropriate control where you can handle it. You can use any control to implement the interface, including user controls. However, it’s not necessary that these controls actually serve any purpose beyond handling the script callbacks. In fact, that’s the approach I’ll take here, using the custom control merely as an event routing mechanism.
Sounds complicated, but it’s actually fairly easy to implement, by creating an EventControl base class. The class implements ICallbackEventHandler and adds an event that fires in response to the RaiseCallbackEvent() and GetCallbackResult() methods firing. This event makes it much easier to handle the event in the context of the executing form rather than in the context of a custom control. Here’s the base class definition:
/// <summary>
/// Control that implements the ICallbackEventHandler interface
/// You can use this class to hookup multiple events per page,
/// one control for each event.
///
/// Use the ScriptCallback event to route the event firing on
/// the control back to your page.
/// </summary>
public class EventControl : Control, ICallbackEventHandler
{ public event delScriptCallBack ScriptCallback; string eventArgument = null; void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) { this.eventArgument = eventArgument; } string ICallbackEventHandler.GetCallbackResult() { if (this.ScriptCallback != null) return this.ScriptCallback(this.eventArgument); return "Not Implemented"; }
}
/// <summary>
/// Delegate that mirrors RaiseCallbackEvent signature
/// </summary>
public delegate string delScriptCallBack(string eventArgument);
You can now define a couple of properties on the form using this base class. One handles the Invoice lookup from the customer selection, and one handles the lineitems lookup from the Invoice selection.
public partial class InvoiceLookup : System.Web.UI.Page
{ public EventControl InvoiceEventControl = new EventControl(); public EventControl CustomerEventControl = new EventControl();
Next these controls are initialized and added to the Page’s Control ParseTree:
protected void SetupScriptCallbacks()
{ // *** Add the Event controls that implement ICallbackEventHandler // *** Attach the ScriptCallback event to pass events into this form this.CustomerEventControl.ScriptCallback += new delScriptCallBack(CustomerEventControl_ScriptCallback); // *** Add control to Control Collection this.AddParsedSubObject(this.CustomerEventControl); // *** Attach the callback handler to the server event // *** via Client onchange Event string ScriptRef = this.ClientScript.GetCallbackEventReference( this.CustomerEventControl, "document.forms0.txtCustomerPk.value", "OnCustomerSelectionCallback", "‘Customer’", "OnCustomerSelectionCallback", true); this.txtCustomerPk.Attributes.Add("onchange", ScriptRef); // *** add the Invoice Event Control so we have a separate handler this.InvoiceEventControl.ScriptCallback += new delScriptCallBack(InvoiceEventControl_ScriptCallback); this.AddParsedSubObject(this.InvoiceEventControl); ScriptRef = this.ClientScript.GetCallbackEventReference( this.InvoiceEventControl, "document.forms0.txtInvoiceList.value", "OnInvoiceSelectionCallback", "‘Invoice’", "OnInvoiceSelectionCallback", true); this.txtInvoiceList.Attributes.Add("onchange", ScriptRef);
}
Each of these event controls gets instantiated and the ScriptCallback event is hooked up to call a method on the form. This keeps the event handling code within the context of the form and you have access to the Page class and its methods directly as opposed to managing the event inside of the control implementation. Once the control is configured it’s added to the Page ‘s parse tree. Finally each callback reference is hooked up to the appropriate client side scripting event; in this case the list onchange() events. They are assigned the result from GetCallBackEventReference() which holds the client function call string that is used to call the ASP.NET client framework script that initiates the script callback and returns the result to your target functions – in this case OnCustomerSelectionCallback() and OnInvoiceSelectionCalback() respectively.
We now have two controls on a form, each of which handles one of the script callback events. The Invoice lookup listing is a little more involved than the HTML examples of the part 1 article.
string CustomerEventControl_ScriptCallback(string eventArgument)
{ string CustomerId = eventArgument; busInvoice Invoice = new busInvoice(); if (Invoice.GetInvoicesForCustomer(CustomerId) < 0) return "Error: " + Invoice.ErrorMessage; StringBuilder Result = new StringBuilder(); foreach (DataRow dr in Invoice.DataSet.Tables["TCustomerInvoiceList"].Rows) { // *** Write out the PK Result.AppendLine( dr["Orderid"].ToString() ); Result.AppendLine( ((DateTime) dr["OrderDate"]).ToString("yyy-MM-dd") + " #" + dr["OrderId"].ToString() + " " + ( (decimal) dr["OrderTotal"] + (decimal) dr["Freight"]).ToString("c") ); } return Result.ToString();
}
This code uses the same business object used in the previous examples to retrieve a list of invoices into a DataTable. It runs through the rows and generates a string list that contains pairs of Pk and a display string that shows the order date, invoice number and order total.
The client side callback response method then takes the string and parses the content into the invoice listbox, by assigning the Pk to the value property and the text to the text property.
function OnCustomerSelectionCallback(Result,Context)
{ List = document.getElementById(‘txtInvoiceList’) if (List) { while (List.length > 0) { List.remove(0); } if( Result.substr(0,6) 'Error:' ) { alert(Result); return; } StringList = Result.split('rn'); x = 0; while(x < StringList.length) { Pk = StringList[x]; if (Pk ‘’) break; Text = StringList[x+1]; AddItem(Text,Pk,List); // *** Skip to the next array item // *** skip 2 – each item is pk/text pair x = x +2; } // *** want to hide invoice detail if (x > 0) { // *** assign value to cause onchange to fire List = document.getElementById(‘txtInvoiceList’); List.selectedIndex = 0; List.onchange(); } }
}
function AddItem(Text,Value,List)
{ var opt; opt = document.createElement(‘option’); opt.value = Value; opt.innerText = Text; // IE doesn’t want to work with text only opt.text=Text; // Mozilla doesn’t want to work with innerText List.appendChild(opt);
}
Most of this code deals with parsing the string value and assigning it to the list. Although there’s not much of it, this is the most time consuming part of the process because debugging in the script environment and dealing with browser compatibility issues is such a pain.
As an alternative you can send the entire select object as an HTML string from the server. If you take at the InvoiceLookup.aspx.cs page, there’s a switch in the source code for HtmlInvoiceList , which switches the invoice list into HTML send mode which is a little easier to deal with, but more importantly much easier to copy and move into another application.
This is only half of the process though. You still need to handle the lineitem display as well. Well, I mentioned I wanted to reuse that invoice list, so here I use the HtmlLineItems() method of the business object again to create my HTML output to embed into the form.
string InvoiceEventControl_ScriptCallback(string eventArgument)
{ int InvNo = -1; if (!int.TryParse(eventArgument, out InvNo)) return "Error: Invalid key passed " + this.txtInvoiceList.Text; busInvoice Invoice = new busInvoice(); if (!Invoice.Load(InvNo)) { return "Error: " + Invoice.ErrorMessage + this.txtInvoiceList.Text; } return Invoice.HtmlLineItems();
}
This code is no different than what I showed for the invoice lineitem popup, except in this case the HTML is embedded into a fixed position on the page via the script code.
<div id="InvoiceDetail" style="display:none;height:175px;width:500px" >
Note that I’m giving the tag a fixed width and height. The width matches the width of the lists above it and the height is fixed to handle about 4 lineitems. This is done so that the page doesn’t jump on every refresh. the fixed size forces the content below the list to stay in the same place as long as there aren’t more than 4 lineitems. This looks much cleaner and enhances the stateful client effect.
As you might expect the client code is very simple. It picks up the HTML table from the server and simply displays it by replacing the <div> tags’ innerHtml property.
function OnInvoiceSelectionCallback(Result,Context)
{ Detail = document.getElementById(‘InvoiceDetail’) Detail.style.display=’‘; Detail.innerHTML = Result;
}
Managing Proper Startup
If you create forms with multiple script callbacks you should take care to properly set the startup state of the form. In the form above the customer list always loads server side via standard data binding. It uses the business object, which then binds to the list box. The customer list never changes on the client. But both the invoice list and the lineitems list are initially empty. You don’t really want to duplicate functionality on the server to populate this data on first load, but rather you should fire the events to make them populate themselves.
To populate the customer list the <body> tag’s onload() event is used.
<body onload="document.getElementById(‘txtCustomerPk’).onchange();">
For the lineitems list, the list must be updated every time the customer changes. So as part of the lineitem update process the last thing that happens is to update the lineitem list by firing the onchange of the invoice list :
if (x > 0)
{ // *** assign value to cause onchange to fire List = document.getElementById(‘txtInvoiceList’); List.selectedIndex = 0; List.onchange();
}
Network Bandwidth
It’s important to understand that this sort of thing causes a very chatty Web interface. When you first load the InvoiceLookup.aspx page, there are four ASP.NET hits that fire in response to this request. Take a look at Figure 2; this is from an application called Fiddler, which is an HTTP proxy that lets you track HTTP requests. Fiddler shows you at a glance which requests are made, their HTTP result codes (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for a list of the possible result codes) and the size of the request. In addition, it lets you look in detail at the headers and body sent by each request. Fiddler is an invaluable tool to debug code that sends data over the wire.

Figure 2. Fiddler
This figure has been reduced in size to fit in the text. To view the full image Click here
You can see from Figure 2 that InvoiceLookup.aspx loads, followed by WebResource.axd (this is an ASP.NET internal resource that provides client side script code that handles the callbacks), and the two InvoiceLookup.aspx callbacks. All of these pages are requested when the InvoiceLookup.aspx page loads for the first time either via embedded links to stylesheets, or JavaScript files or via script initiated callbacks to the server.. It’s an interesting exercise to look at WebResource.axd as it contains the complete – and complex – client script logic that manage the script callbacks from the server. In ASP.NET 2.0 Microsoft has removed any external dependencies on JavaScript files, and instead is using WebResource.axd to load any script and other resources which leaves ASP.NET without any external dependencies, which is very welcome!
The content of the WebResource.axd returned JavaScript is fairly involved and consists mostly of generic code that manages the XmlHttp interaction from the client to the server. The client script process goes through a high level wrapper method that wraps up the POST data and then assigns it to the XmlHttp object, which then goes out and asynchronously sends the data to the server. This high level routine sets up the callback handler in the script code, which receives the results from the server’s process. The server is called via HTTP and responds back, and XmlHttp calls back to this internal return handler method. The handler unpackages the result data – which is not the raw string your server code returned, but can also contain a validation key and an identification prefix – and then routes the data back to the callback handler you defined in your call to the GetCallbackEventReference() . This code has been in flux for every .NT 2.0 version Microsoft has released to date so this explanation is somewhat high level – if you want to see more specific detail, take a look at the source itself in WebResource.axd .
By the way if you’re not familiar with Fiddler, building client callback applications is a good reason to take a closer look at this tool. It provides very useful information on the data that you are actually sending over the wire. As you can see it shows you the POST data that is sent from the client script as well as the HTTP response that gets returned. This is even more useful if you get an error on the server side because ASP.NET still generates an ASP.NET error page. If you’re using callbacks there’s no way to see the error message. Using Fiddler or some other HTTP tracing tool you can view the generated output and preview the error HTML.
Callbacks in Perspective
Now you’ve seen how to use callbacks in a more advanced way, I’m going to spend some time evaluating the technology, looking at it’s pros and cons, and seeing what alternatives are available. A very important advantage to callbacks is that if you plan on taking advantage of Microsoft’s Atlas AJAX framework when it is released in 2006, and you wish to ensure that your applications are written in such a way as to be easy to port to Atlas, Microsoft is recommending that you use callbacks as appropriate as part of this process. On the other hand, there are some important limitations of using callbacks.
Limitations of Callbacks
Overall client script callbacks are a welcome addition to ASP.NET but there are some limitations and drawbacks. I personally think the implementation is more complex than it needs to be, especially in regards to multiple callbacks and in the way all of the postback issues work. While it’s not difficult to implement callbacks once you know how, implementing the solution of using separate controls is not exactly obvious.
There are also some limitations of the interface. You have very little control over the callback process, and basically Microsoft is managing this process for you end to end. For example, you can only call back to the same ASPX page – you can’t call back to any other page or even another site.
I mentioned some POST data issues in the previous article. In addition to the funky behavior and complex rules that I mentioned there that make up how the POST data is returned there’s also the overhead of POST data to consider. If you’re only using the eventArgument as input for your processing why should you need to send all this POST data to the server? There’s no way to turn it off even if you don’t use it. This is especially troublesome if you have a large ViewState on the form. Make sure you’re ViewState is under control if you’re using Script Callbacks.
Another problem is how the data travels over the wire. I think XML might in many situations be the best way to send data between the server and the client, but unfortunately Microsoft doesn’t make the XmlHttp object and its XML properties available directly.
Finally keep in mind that script callbacks have a fair amount of overhead. There’s a lot happening to route inbound callbacks to your class and then to the appropriate server side callback method. The ASP.NET code touches all of the ASP.NET HTTP pipeline and most of the page pipeline. If you have a lot of callbacks – especially popup type callbacks – this can very quickly become a burden on your server and possibly your database as well. Make sure you understand the load a callback-based design will incur on your application – it’s real easy to get carried away with this technology and start building things like a Web based chat client, which is just a bad idea <g>.
Alternative Techniques
Before I finish, let’s have a look at a few other ways that you could obtain similar functionality to callbacks.
Doing it Yourself
Creating your own lightweight wrappers around XmlHttp calls is not difficult to implement and gives you more flexibility in calling back to the server. If you take a look at the code returned from WebResource.axd on a script callback event you can see that the logic required to make the actual XmlHttp calls is not overly complex, so it’s straight forward to build a custom solution. If you do that, things like calling out to other pages beome much easier and you’ll probably also pass much less data from the client to the server. You can also isolate display logic rather than forcing the Page to have to handle multiple display mechanisms (main ASPX display and any callbacks).
All the control state information can be useful if you choose to use it but if you don’t it’s just overhead. None of my examples here used any client state other than what was passed in the event argument, yet ASP.NET passed all the form data from the client to the server on each call eating valuable bandwidth. You can’t turn it off!
As I started playing with ASP.NET client callbacks I found a few uses I wanted to use in 1.1 applications, and it was pretty easy to build a control that performs most of the features script callbacks provide with the ability to call other URLs, pass XML and even extend it to automatically to create hover windows from the result of a url. By writing your own component you gain flexibility instead of a black box solution and it’s not difficult to do.
AJAX Libraries
Microsoft is not the first to the .NET AJAX party and there are actually several tools available to do AJAX with .NET. Specifically I like Jason Diamond’s free My Ajax Library (see links section) which also uses a page based model like script callbacks, but is much easier to use. It’s attribute based, and you basically create methods in your ASP.NET Page class that are attributed with a special AJAX flag. The class then proceeds to map those server side methods to an object and client side methods that you can call from your script code. It’s a very intuitive approach and requires very little code. In addition, it posts back current values and allows you to send some useful object types from the server to the client including Arrays (IEnumerable), DataSet/Table/Row and simple types, all as true types rather than strings. The library is fully self contained in a single source file and can be embedded into any project for free. There’s also Michael Schwarz’s popular and free Ajax.NET library (see the links section) which also uses an attribute based approach. It’s a bit more complex than Jason’s library and requires some special setup as it uses a custom HTTP Handler based approach that’s completely separate from the Page pipeline. In addition, several other free and commercial AJAX libraries are starting to appear. One advantage with all of these libraries is that they also work with ASP.NET 1.1, so you can use them today.
Finally Microsoft is also readying a new framework called Atlas that is supposed to provide vastly expanded AJAX support for ASP.NET. At the time this article was written very little information was available, so it’s hard to say what to expect. Atlas will ship as an add-on to ASP.NET and ship after .NET 2.0 releases. It’s not based on script callbacks, but very little additional information is available at this time. More details will be released at Microsoft PDC when Microsoft is expected to showcase this technology in detail.
This doesn’t mean that it’s going to make script callbacks obsolete however. Script callbacks work and more importantly are built directly into ASP.NET. They are an acceptable native solution and are especially attractive if you’re writing controls as the functionality is baked into the ASP.NET framework at the control level and therefore available to control developers. Script callbacks are used extensively by some of the dynamic controls in ASP.NET 2.0 such as the TreeView for example.
Conclusion
In this article I’ve introduced the concepts of making client script callbacks and I’ve shown you how to use Microsoft’s implementation in a number of different ways. As time goes on I’m sure the developer community is going to find more uses of this technology as well as more efficient ways to implement it in a reusable way. At the moment the technology looks like it’s geared more towards control developers, who can integrate client callback functionality into their controls. Microsoft internally is using this feature extensively for pulling data dynamically for their own controls like the new TreeView for example.
More than anything though, the key with callback technology is to apply it appropriately. Because the concept splits code between client and server, and script technology still is awful, using script callbacks is by nature more complex than pure server side technology. So be careful to not overuse it and make your application more complex than they have to be just for a ~cool effect’. Take a look at the technology, and see where it applies to your needs It can solve some big technical issues if applied correctly.

