Article Author: Rick Strahl
Introduction
Web applications are stateless and tend to use a straight request and response mechanism. Web page users view a page and to perform an action `1click on a link or button to make something happen. In traditional HTML based, dynamic Web applications this process involves taking the request back to the server, interpreting the user’s input, performing the necessary processing and then creating a whole new version of the user interface in HTML format. In this typical scenario the Web application serves as both the UI generation layer and the business layer.
What’s Wrong with Postbacks?
Although postbacks represent the most common scenario, they are also pretty inefficient if you think about it for a minute. On every request the server has to re-create the entire user interface, so there’s extra processing required to create the HTML markup, and the server has to feed the HTML back to the browser over the Internet connection, which consumes bandwidth. In most cases this is data and HTML that is already on the client in the first place and is just being resent over and over again. There’s also the issue of user interface ~flash’. That’s your user interface redrawing as the page is refreshed from a server operation. It is a cosmetic issue, but annoying nevertheless.
Finally there are some real logistic issues. There are scenarios where traditional postback mechanisms simply don’t work well, or require extensive workarounds. For example, think of a page that has a number of list boxes or drop downs all of which depend on the value of other fields on a page to fill their data. In the disconnected Web application running in the browser, each of those dropdown list choices requires a postback to the server to refresh the entire page interface. Since each of these dropdown lists might contain a fairly large set of data, pages can become unmanageably large very quickly resulting in very sluggish page performance. A simple example here is a list of cars, year of make and models. There are thousands of models, but on a page you might only see a make (manufacturer’s name) and range of years, together with just the models for that make and year. With a traditional postback mechanism you’d have to post back for every change made to the make or year and redraw all of the data in each of the lists. This means many postbacks of lots of data, which is very inefficient and makes for a slow and user unfriendly user interface.
Getting Around Postbacks
There are ways to address some of these issues today. Recently there’s been a lot of talk about AJAX (Asynchronous Javascript and XML), which is based around a fairly simple concept of using client-side script based HTTP callbacks into server side code to update client side content. The technology is really nothing new. Microsoft has been shipping its XmlHttp client HTTP component since 1997, but the idea has gained new popularity recently because of Google’s high profile services that use it and, maybe more importantly, the fact that most recent browsers including the Mozilla browsers and Opera now also include XmlHttp functionality natively.
With a common HTTP client, it’s much easier to access server content from within a page that runs on the client’s browser via script code. The idea is that you can update parts of the HTML page content by going to the server and retrieving additional content, and then refreshing the current page with the downloaded content. This content can be either HTML that gets embedded into the page or more low level data such as XML or string delimited lists that the client code can parse and embed into the page.
Getting back to our cars and models example, you could load up the page with empty lists and then retrieve the content of each of the lists as needed. The car makes would load as part of the page since they are constant. But the other lists should load dynamically. As the first car make is selected, an out of band call can retrieve all the years that are available for that make. As a year is selected the models are retrieved. Each of these calls goes back to the server, retrieves content in some format such as XML or a delimited string, and then uses this content to fill the lists using client script code to drive the content into the HTML document that is already on the client. Only the content that changes is updated rather than the entire page.
This is a two part article, here in part one I will introduce the basic concepts as well as develop several examples to help you understand the basic principles. In part II I will develop a more complex example that uses multiple callbacks, and I will also discuss some of the pros and cons of using this technology.
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
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/.
Understanding Client Script Callbacks
ASP.NET 2.0 introduces AJAX-like functionality with a new feature called Script Callbacks , which provides a server side wrapper around some of the low level details of the AJAX implementation. By using this mechanism you don’t have to worry about the low level aspects of calling XmlHttp to explicitly call back into your server. Instead ASP.NET manages the callbacks for you and your code implements client side and server side event handlers.
In order to create a page with Script Callbacks you need to do the following:
- Implement the ICallbackEventHandler interface on your ASP.NET page or control
- Create a couple of methods that implement the RaiseCallbackEvent() and GetCallbackResult() interface methods
- Create a client side script handler method that receives the response
- Attach the callback event reference to client side event handler or code snippet
This is not the most straightforward approach that you could possibly think up, but it’s fairly easy to implement once you understand the basics of the process.
A Very Simple Example
Let’s start with a simple example of how Script Callbacks work. It’s an ASP.NET form with a button on it that retrieves the server time and pops up a JavaScript alert() dialog with the result.Figure 1. shows the output from the button click. Whohoo! Let’s take a look what happens behind the scenes.

Figure 1. A very simple Script Callback that fires from a button click
The button is actually NOT an ASP.NET button server control since you don’t want the form to post back when the button is pressed, instead it’s a plain HTML control.
<input type="button" value="Get Server Time" runat="server" ID="btnCallServer" />
Page loaded on: <%= DateTime.Now %>
In addition there’s also an expression that displays the date and time when the page is first rendered, so we can see that the page is not refreshed when making the script callback. Other than that the ASPX page contains only static HTML.
For this simple example you can implement all the logic on the server side. Here’s what the codebehind looks like:
public partial class SimpleCallBack : System.Web.UI.Page, ICallbackEventHandler
{ protected void Page_Load(object sender, EventArgs e) { this.SetCallBackString(); } protected void SetCallBackString() { // *** Attach the callback handler to the server event // *** via the Client Event string ScriptRef = this.ClientScript.GetCallbackEventReference( this, "‘TimeCallback’", "OnCallback", "this", "OnCallback", true); this.btnCallServer.Attributes.Add("onclick", ScriptRef); // *** Create the script that fires in response to the call back on the // client // *** Normally you’d create this in the HTML code this.ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientCallback",
"<script type='text/javascript'> function OnCallback(Result,Context) { alert(Result); } </script> "); } // *** Capture the event argument in this field string eventArgument = ""; // *** Initiate the callback by capturing the event argument void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) { this.eventArgument = eventArgument; } // *** Generate the callback result as a string string ICallbackEventHandler.GetCallbackResult() { return "Hello from the Server. Time is: " + DateTime.Now.ToString() + "rnPassed event argument: " + this.eventArgument; } } <p></pre><!--@END —>
The ICallbackEventHandler interface is implemented on the Page class and the class implements the required RaiseCallbackEvent() and GetCallbackResult() methods. RaiseCallbackEvent() acts as the input handler that receives an event argument that is a string. This value needs to be saved so that the GetCallbackResult() method can use it later to perform its processing. The eventArgument is a string value that the client sends to the server as a parameter. Typically this value can be an ID or value captured on the form. In the simple example above, there’s really nothing that needs to get passed, but just for kicks I’m sending a static string value TimeCallback as a parameter. GetCallbackResult() then is responsible for creating the response. This method returns a string, which is sent to the client and becomes the result value of the callback.
Before this call can be routed to the server side method, a CallbackEventhandler must be hooked up to a client side event or script code. The resulting function reference is required to trigger the event sequence that causes your server side code to be called and the result to be returned. Retrieving the function reference (as a string) is accomplished by the ClientScript.GetCallbackEventReference() call above, which is passed a number of parameters:
- A reference to the control handling the callback event
- A parameter or context string
- The function on the client to call when the call completes
- The client side context for the callback (usually this )
- The function on the client that is called when an error occurs
- A Boolean flag that determines whether the call is asynchronous
The output script code string from GetCallbackEventReference() looks like this (all in one line):
WebForm_DoCallback(’__Page’,‘TimeCallback’,OnCallback, this,OnCallback,true)
This event reference string is then hooked up to the onclick event of the button on the server side via the Attributes collection of the button. The WebForm_DoCallback() function does all the hard work, it makes the XmlHttp call to the server, which routes the request to the RaiseCallbackEvent() and GetCallbackresult() methods on the server. The call then returns to an internal handler on the client side with the string result from the server. The result along with the context parameter specified for parameter 4 is then passed to the client side function you specified in the second parameter of GetCallbackEventReference() .
The two key items of the long parameter list to GetCallbackEventReference() are firstly the parameter context – which gets passed to the server – and secondly, and the callback method that gets fired in response. The parameter context is a value that you can pass from the client to the server and this value can either be a hard coded value as in the example above or an expression that is in scope on the client side when the callback is made. For example, if I want to pass the caption of the button I can pass the following as the second parameter to embed into GetCallbackEventReference() :
document.forms0.btnCallServer.value
Note that because this is an expression there are no quotes around it, and the expression is evaluated at the time of the callback. The call simply embeds the result of the expression into the function call, so anything that is in scope and can evaluate in that context works. You can only pass a single parameter, so if you need to pass something more complex, it’s your responsibilty to encode the parameter or expression. To do this you typically write the string to a global user defined variable and then use a script variable as the expression to pass as the parameter.
The callback method is the client side script method that is called when the XmlHttp call against the server returns. The method receives two input parameters – Result and Context – which repectively contain the string that the server returns and a Context value which is the value or expression specified in the 4th parameter of GetCallbackEventReference() . The function needs to support this signature in client script:
<script type=‘text/javascript’>
function OnCallback(Result,Context)
{ alert(Result);
}
</script>
This is really the key method that is used to do something useful with the content returned from the server.
Note that I created the client event handler on the server side using RegisterClientScriptCode() in this example. I did this to keep the code all in one place for clarity, but for more complex handlers you probably want to implement the callback function in the ASPX page as a script block.
What About Postback Data?
In the example above I simply passed a parameter from the client to the server and the client picked up the eventArgument in the RaiseCallbackEvent() method. The actual parameter is passed to the server as part of a complex POST buffer that is passed back on each callback. The POST buffer contains an Event ID and the Event argument, as well as a long string for event validation.
ASP.NET also POSTs back form data to the client. Your first thought might be : Cool, I can access my form controls and the Request object with current values from the page. Say you had a form with a few textboxes and you wanted to save the data from these fields in a callback. It’d be great if you could just access, say, this.txtCompany.Text and read the value.
The behavior of data sent back has changed considerably between Beta 2 and August CTP. Beta 2 sent querystrings, while later CTPs POST the data back. In both cases the data sent back is the form data from the last Postback. The code with this article supports both Beta 2 and post Beta 2, but the bottom line is that the data sent back for either is not very useful.
Microsoft claims that there are consistency issues in posting back updated form data in a callback. There can be issues with ViewState, haphazard OnChange behavior and major issues with complex controls like the TreeView which rely on more than just post data to update their state. Unfortunately, this also makes the postback data absolutely useless for reading client state. It’s really a shame that the behavior of a few complex controls overrides this useful behavior for simpler controls, but Microsoft as an API vendor apparently requires consistency more than a more functional solution that would work just fine in 90% of the cases.
In short, don’t use the POST data unless you don’t need the latest current values from the client.
Also be aware that POST data by default includes an enableEventValidation flag on the page which can be set in the Page directive on the HTML page. If true this flag embeds a rather large validation string into the POST data which is used to ensure consistency of the data by posting this data to the client and then sending it back on the response. Quite frequently you will get a failure after a callback if you go ahead and postback the form. When you see this error, make sure you set the EnableEventValidation flag to false on the page.
A More Useful Example – HTML Hover Windows
Now that you understand how the basic callback mechanism works lets look at a more useful example. One of the things you’ll notice about script callbacks is that they work exclusively with strings. One string input and one string output specifically. One of the first things you need to decide is how to format the string data you pass back and forth. The first approach I’ll show is deceptively simple: Passing back HTML markup to embed into the client page.
One very popular, and cool effect that’s ~popping up’ a lot recently is the HTML hover window. This effect is a little pop up window that pops on top of an existing HTML page and provides additional information.
This is an effect that you can quite easily achieve by using script callbacks. Take a look at Figure 2 to see what the result of the following example looks like. It displays a list of invoices from the Northwind database and then, without refreshing the original page, pops up the invoice detail information on top of the list as you hover over an invoice link. As you move off the link the window goes away.

Figure 2. Using a Popup HTML windows to quickly display context sensitive data
The form in Figure 2 consists of a GridView control with content filled from the Northwind Database via a business object method that returns a dataset and is bound to the GridView . To implement the script callback, the Page implements the ICallbackHandler interface. Here’s what the RaiseCallbackEvent() and GetCallbackResult() methods looks like:
string eventArgument = "";
public void RaiseCallbackEvent(string eventArgument)
{ this.eventArgument = eventArgument;
}
public string GetCallbackResult()
{ // *** Use ‘static’ Html generation in business object int OrderId = -1; if (!int.TryParse(eventArgument, out OrderId)) return ""; // *** Create Invoice Bus Object and load Order busInvoice Invoice = new busInvoice(); if (!Invoice.Load(eventArgument)) return ""; // *** Generate Html for lineitems return Invoice.HtmlLineItems();
}
The event argument is the OrderId , which is embedded into each of the GridView items and then passed to the callback event function in the client script. More on this in a minute. The HTML returned is an HTML table of lineitems, generated by a routine in a business object, which uses this HTML generation for a number of display purposes. It’s just raw HTML generation with a StringBuilder that creates a fancy HTML table for a given order id (you can find the source in the file busInvoice.cs of the sample code).
The CallbackEventReference is hooked up by the page and stored in a class property to be embedded into the ASPX HTML. This code is called from Page_Load() , and the event reference is then embedded into the ASPX script code via expression tags.
void HookUpEventReference()
{ this.CallBackEventReference = this.ClientScript.GetCallbackEventReference( this, "OrderId", "OnLineItemView", "‘LineItemView’",true);
}
OrderId refers to a script variable that is set in the script code that is passed to the script ShowLineItems() function. The code then calls into the OnlineItemView() method in the script to display that content. The script code in this scenario is a bit more complicated. It receives the HTML and uses an HTML <div> element with absolute positioning to display the lineitems at the current mouse cursor position. The absolute positioning forces the <div> tag to appear on top of the base page content.
The <div> tag is defined as non-visible at an arbitrary absolute position to start with:
<div style="position:absolute;top:0;left:500;width:500;display:none" id="LineItemPlaceHolder"></div>
The hover operation then is initiated by the onmouseover and onmouseleave events of the href links embedded into the Gridview as a template column.
<ItemTemplate>
<a href=‘ShowInvoice.aspx?id=<%# Eval("OrderId") %>’
onmouseover=‘ShowLineItems("<%# Eval("OrderId") %>",event)’
onmouseout=‘HideLineItems();’><%# Eval("OrderId") %>
</a>
</ItemTemplate>
Note that rather than embedding the CallbackEventReference string directly, I use a client side function – ShowLineItems() – to pass the OrderId . This is done to provide more control over the event call as you need to handle a few additional steps such as keeping track of the mouse location.
The actual script code in the page consists of a couple of helper functions as well as the actual event handler method. Here’s the complete script code:
<script type="text/javascript">
var LastMouseTop = 0;
var LastMoustLeft = 0;
function ShowLineItems(OrderId,event)
{
if (event != null)
{
LastMouseTop = event.clientY;
LastMouseLeft = event.clientX;
}
<%= this.CallBackEventReference %>;
}
function HideLineItems()
{
document.getElementById(‘LineItemPlaceHolder’).style.display = ‘none’;
}
function OnLineItemView(Result,Context)
{
var Panel = document.getElementById(‘LineItemPlaceHolder’);
Panel.innerHTML = Result;
if (LastMouseTop != 0)
{
Panel.style.top = (LastMouseTop + 5).toString() + "px";
Panel.style.left = (LastMouseLeft + 5).toString() + "px";
}
Panel.style.display = ‘’;
}
</script>
ShowLineItems() is a wrapper around the CallbackEventReference() function call. If you recall, the callback reference is that long WebForm_Callback() function call that ASP.NET generates to initate the script callback. The wrapper makes sure we store the last mouse position and so reduce the amount of HTML that gets embedded for each item in the GridView. The actual function call is embedded as an ASP.NET expression ( <%= %> syntax). Notice the event parameter, which is passed from the onmouseover event handler to ensure the code has access to the mouse position of the event object.
The expanded code inside of the ShowLineItems() functions looks like this:
WebForm_DoCallback(’__Page’,OrderId,OnLineItemView, ‘LineItemView’,null,true);
The ShowLineItems() method initiates the callback to the server. The OnlineItemView() function picks up the HTML returned from the callback and assigns it to the LineItemPlaceHolder <div> object. The object is set to be visible and the mouse position set and voila – the popup is visible on top of the Invoice list page.
It takes very little code and the process is fairly generic, so that, other than the object names used, you can almost cut and paste the code for the client and server into any application and have it work on most browsers that supports the XmlHttp object and absolute positioning, which includes most recent browsers including Internet Explorer, Mozilla based browsers, Opera and Safari (which are all that I’ve tried with).
Aside from being a flashy feature, it also is very useful. Without callbacks usually the process of getting the same information involves clicking on a link displaying the invoice, then when done clicking another link to get back to the list – often losing your place in the list. Compare that to the callback that pops up in context on top of the page. Not only does this present information more quickly, but it also maintains your place on the original page. It’s a very efficient way to display relevant context sensitive information.
But it’s also important not to abuse this mechanism as it can get annoying very quickly if used excessively or inappropriately. Always make sure that the hover operation actually provides useful content. Also remember that even though the content pops up on top of your page there’s still a server hit involved in this process, so if your users go hog wild hovering over links you might be incurring excessive load on your server.
Using ASP.NET to Generate HTML in a Callback?
In the last example I hand coded HTML in my ~business object’. Handcoding HTML is rarely a good idea, but I do it here because I want to re-use that the lineitem list in several other examples. Another option that works well for more complex scenarios if you need to reuse code is to offload processing to another ASP.NET page altogether. But how do you do that? Script callbacks always post back to the same page that originated the request.
While you can’t make a script callback call back to another page, you can make your current page call another page for processing by using Server.Execute() . It let’s you call another ASP.NET page from the current page and write the results into a TextWriter .
To demonstrate I’ll enhance the last demo by adding a checkbox to the form to Show Full Invoice Page . When checked the callback method calls out to another page called ItemList.aspx to render a more complete invoice view that shows the invoice number and customer information. By using an external page for rendering, you can use VS.NET or any HTML editor to lay out your HTML. You can also test the page independently. The page created is really a page fragment. It’s a full ASPX page without the HTML headers and the body tag, leaving just the actual content – usually a table or a div tag with the actual HTML. You can check out the InvoiceAbstract.aspx page to see what I mean.
From the codebehind end, here’s how it works. I added some conditional logic to RaiseCallbackevent() based on the checkbox – when it isn’t checked the form just displays the lineitem list as before. But when checked it calls a method called AspTextMerge() that goes out and executes any ASPX page in the current application and returns the result as a string.
public string GetCallbackResult()
{ if (!this.chkFullInvoice.Checked) { // *** Use ‘static’ HTML generation in business object int OrderId = -1; if (!int.TryParse(eventArgument, out OrderId)) return ""; // *** Create Invoice Bus Object and load Order busInvoice Invoice = new busInvoice(); if (!Invoice.Load(eventArgument)) return ""; // *** Generate HTML for lineitems return Invoice.HtmlLineItems(); } else { // *** Call out to another ASPX page to handle // *** HTML generation string Output = AspTextMerge("InvoiceAbstract.aspx?id=" + eventArgument, out ErrorMessage); return Output; }
}
public static string AspTextMerge(string TemplatePageAndQueryString, out string ErrorMessage)
{ string MergedText = ""; ErrorMessage = ""; // *** Save the current request information HttpContext Context = HttpContext.Current; // *** Fix up the path to point at the templates directory TemplatePageAndQueryString = Context.Request.ApplicationPath + "/" + TemplatePageAndQueryString; // *** Now call the other page and load into StringWriter StringWriter sw = new StringWriter(); try { Context.Server.Execute(TemplatePageAndQueryString, sw,false); MergedText = sw.ToString(); } catch (Exception ex) { MergedText = null; ErrorMessage = ex.Message; } return MergedText;
}
The AspTextMerge() function is static and ideally you’ll want to store it in a utility class so you can easily reuse it . It comes in handy for many operations. This method takes an application relative page path and executes that page. It writes the output to StringWriter and returns the string to your code which can then echo it back as a result from a script callback. This is one of the easiest ways to generate HTML output.
Use HTML Results Whenever You Can!
In this example, I am superimposing HTML ontop of an existing page. It’s easy to generate the HTML on the server, and it’s just as easy to consume this HTML on the client, because you don’t have to parse the HTML. You simply embed it into the existing page. It’s simply easier to do this, than trying create a custom ~protocol’ to pass the data and then parse it in client script.
This doesn’t just apply to obvious full HTML snippets either. You can use this same technique to replace the content of existing controls like a list object for example. You can replace the innerHtml of a <Select> object and generating just the inner <option></option> entries for the list. It’s more work on the server and creates potentially larger callback content, but it’s much easier to create and debug the code on the server.
Whenever you can take client script code out of the equation, you greatly simplify the development process and in most cases (especially compared to XML) you don’t even incur a greater Http payload.
Conclusion
Script callbacks in ASP.NET introduce a powerful new mechanism to extend existing applications with client side generated events that are handled on the server and can feed the results back to the loaded HTML page. This AJAX style mechanism makes it possible to create pages that are more lightweight in the data passed around as well as providing the ability to create quicker and less intrusive user interfaces for the user.
In this article I’ve introduced script callbacks in ASP.NET by showing how the technology works and describing a few relatively simple examples. In the second half of this series, I’ll look at a more involved example that includes multiple callbacks from a single page, and uses a custom messaging scheme to pass data across the wire, which involves more client side logic. I’ll also discuss a few thoughts on the pros and cons of the ASP.NET implementation. Until then check out the basic features and see what you can accomplish with very little code in ASP.NET 2.0 with script callbacks.

