Script Callbacks in ASP.NET 2.0

Let’s face it: Web developers would give anything for a programmable tool that would allow them to avoid page refresh. Imagine the following, rather common, scenario: You add a grid control to an ASP.NET page and make it show users a navigation bar. Whenever the user clicks to display a new set of rows, the page posts back, performs some work on the server, and then reappears identical to the previous time (except for the new set of grid rows). This process carries a significant performance hit, especially for large and complex pages. Why on earth, developers wonder, should 50 Kb of stuff have to be downloaded for each user action (and a good part of it also uploaded with the same frequency)?

Being able to update client data without causing the entire page to post back is an old dream for Web developers. Ideally, they should be able to wire up client code to call the server, execute an event, take the returned data, and update only the portion of the client page touched by the action.

ASP.NET 1.x and classic ASP enabled this function, but the developer had to accept an ActiveX control or at least a Java applet on the page to act as an intermediary. The intermediary received calls from the client’s script code and set up a parallel, invisible connection to a server page. The server page got some input and returned some output. The output returned through the invisible connection was processed on the client and used to update the page via the Dynamic HTML (DHTML) object model.

ASP.NET 2.0 abstracts the developer from the creation of the request to the server and the logic needed to parse the server’s response. Script callbacks in ASP.NET 2.0 offer a ready-to-use mechanism that greatly simplifies the procedure. More importantly, they hide a lot of the implementation details and shield you from a bunch of browser issues.

Script Callback Requirements

At the core, ASP.NET script callbacks consist of some client-side JavaScript code that arranges for a programmatic roundtrip to the server. So, the first requirement for using the callbacks is that some server-side code awaits client calls. The client-side callback intermediary manages the call while the user continues to see the old page and interact with it. The callback mechanism leaves the current page unaffected and gives users the illusion that everything is taking place on the client like in a classic desktop application. Hence, the second requirement is that the browser provides DHTML capabilities and offers an advanced DOM implementation. Without these capabilities, any downloaded information would mostly be useless.

In the Microsoft world, the first implementation of callbacks was Remote Scripting (RS). RS leverages a Java applet to connect to the server and requires an ASP page to serve the request. In addition, the ASP page provides a made-to-measure object model—a sort of public and common interface—for the interaction to take place. In ASP.NET, the overall model is similar but the tools employed are different.

How ASP.NET Script Callbacks Work

ASP.NET callbacks use the XmlHttpRequest DOM object to set up the connection. (As far as Internet Explorer is concerned, script callbacks require at least version 5.0.) The target of the remote invocation can be either a particular server control (for example, the new GridView control) or the page itself. In general, the target must be an object that implements the ICallbackEventHandler interface.

The following paragraphs show how to enhance a page to make it support script callbacks.

The first step is identifying the HTML element that triggers the operation. Typically, this element is a button or a link that users click. It is essential that the element fires an event that is not automatically handled by the browser and results in a postback. For example, you should never use an <asp:button> element to trigger the callback because <asp:button> generates a submit button. The following code is fine, if you want to start the operation with a button click:

<input type="button" runat="server" id="callbackStarter"
       value="View Details">

This button—an instance of the HtmlInputButton control—needs some special JavaScript code to handle the click event. You can add the following code programmatically in the Page_Load event:

callbackStarter.Attributes["onclick"] = String.Format("javascript:{0}",
       callbackRef);

How do you determine the right script code to bind? The GetCallbackEventReference function on the Page class can help. Here’s an example:

string callbackRef = GetCallbackEventReference(
    this,
    "document.all['cboEmployees'].value",
    "UpdateEmployeeViewHandler", "null", "null");

The GetCallbackEventReference function’s first argument indicates the target object that will handle the call on the server. If you pass this, you mean the page itself. You also can pass in a reference to any page control that implements the ICallbackEventHandler interface. In any case, the client-side submit action will hit the same ASPX page through the standard postback mechanism.

The second argument is a constant or a JavaScript expression that represents the input sent to the server. In the code above, you pass the value of the currently selected element of a drop-down list. (See the full source code.)

The third argument is the name of a user-defined JavaScript callback function defined in the <script> section of the page. This function ultimately will be responsible for updating the page after the callback is made. The last two arguments optionally can provide an error handler and a context object.

The GetCallbackEventReference function generates a script call like this:

WebForm_DoCallback(
    pageID, input, UpdateEmployeeViewHandler, null, null);

The source of the function is automatically downloaded on the client through a <script> tag that is silently added to the page’s response. WebForm_DoCallback uses the XmlHttpRequest DOM object to call back the URL of the current page. In doing so, though, it appends a couple of extra hidden fields to let the server code know that the request is not a classic postback, but a simpler (and lightweight) callback.

When serving the request, the ASP.NET runtime code identifies the target of the call (the first parameter to GetCallbackEventReference), ensures that it implements the ICallbackEventHandler interface, and invokes the RaiseCallbackEvent method:

public virtual string RaiseCallbackEvent(string eventArgument)
{
    // Do something with the input and generate the output
    return ProcessTheCall(eventArgument);
}

RaiseCallbackEvent takes and returns a string. It also contains any code you need to run on the server in response to the client’s input. The data exchange between the client and the server can happen only through strings. A string, though, can contain anything, including serialized objects. The format of the exchanged strings is completely up to you. What RaiseCallbackEvent returns is delivered to the JavaScript client callback (UpdateEmployeeViewHandler in the preceding code). Here’s the required prototype of the function:

function UpdateEmployeeViewHandler(result, context)
{
    // "result" is the return value of RaiseCallbackEvent

    // TO DO: Process the string and update the page using DHTML
}

As mentioned, script callbacks are not an all-browsers feature, although it works with the most recent browsers, including Internet Explorer 5+, Netscape 6+, and Safari 1.2+. Microsoft has added two new browser capabilities to ASP.NET 2.0 to let developers check the applicability of the solution: SupportsXmlHttp and SupportsCallback.

Faster Page Refreshes with One Caveat

Although defined for version 2.0, ASP.NET script callbacks are not too hard to port back to ASP.NET 1.1. In ASP.NET 2.0, a good number of server controls take advantage of this feature to provide faster page refreshes. The most illustrious example is perhaps the GridView control, the successor to the DataGrid, that optionally uses callbacks to page through displayed records.

As mentioned, script callbacks rely on the DOM’s XmlHttpRequest object. In Internet Explorer, this DOM object is implemented through an ActiveX control, Microsoft.XmlHttp. To view a page through IE, you need to weaken your security settings and enable ActiveX controls to be safe for scripting. This is unnecessary with other browsers that implement the same XmlHttpRequest DOM object. Mozilla-powered browsers, in fact, implement the HTTP request capabilities internally without resorting to an ActiveX control—a capability that will hopefully come with Internet Explorer 7.0.

About the Author

Dino Esposito is Wintellect‘s ADO.NET expert and a trainer and consultant based in Rome, Italy. He runs the Cutting Edge column for MSDN Magazine and is a regular contributor to MSDN News, Visual C++ Developers Journal and SQL Server Magazine.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read