Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implement Script Callback Framework in ASP.NET 1.x

0.00/5 (No votes)
2 Aug 2004 3  
It allows calls to server events from client script code without causing the page to post back and refresh.

Sample Image

Introduction

One of the challenges that every ASP.NET 1.x web developer faced is that there isn't any native support for script callbacks to server without post-back of the current page. For example, retrieving and presenting the details of a selected employee in a dropdown list without forcing the full page refresh, and this is particularly an expensive call for UI-rich pages. So, how can we handle this?

Thanks to the ASP.NET team, they've built in this feature in ASP.NET 2.0, which allows calls to server events from client code without causing the page to post back and refresh. This is great, but we have to consider 2 facts: Time and Money. For time, we have to wait until ASP.NET 2.0 is released and we can't do anything in the meanwhile. For Money, we may be forced to upgrade Visual Studio .NET IDE from 2002/2003 to 2005 if we can't live without the Visual Studio.NET IDE. More importantly, implementing the script callbacks in ASP.NET 1.x isn't that hard at all. So, what are we waiting for?

Thanks to the extensible architecture of Page Controller framework, it gives developers the flexibility in hand to extend its functionalities, where native support is not sufficed. In this article, I'll walk you through the process of implementing the Script Callbacks Framework, and also provide a simplified but effective framework that you can use in ASP.NET 1.x.

Objectives

Several objectives have been set for the implementation of Script Callback Framework in ASP.NET 1.x, they are:

  • Co-exists with the same functionality provided in ASP.NET 2.0 - avoids the use of similar function or variable name in our implementation.
  • Provide a consistent programming model as ASP.NET - similar to the concept of Page.GetPostBackEventReference function.
  • Emulate as many functionalities as Script Callbacks Framework implemented in ASP.NET 2.0.
  • Provide synchronous and asynchronous callback.

Background

Script Callback Framework consists of 2 core components, which are PageTemplate.cs and ScriptCallback.js files. Each of them takes care of the callbacks processing at server side and client side, respectively. The steps involved in callbacks processing are shown below:

  1. Script preparation - Generate and attach the callback JavaScript code to Control's event.
  2. Script callbacks - Invoke the embedded JavaScript, which in turn opens the HTTP connection to the specified remote ASP.NET Page whenever the control's event is triggered.
  3. Server responses - Lookup and invoke the corresponding control to respond accordingly.
  4. Callback handling - Invoke the client Callback Handler or Error Handler based on the status code returned from the server's response.

Script preparation

PageTemplate derives from System.Web.UI.Page where it encapsulates most of the processing logic and plays a critical role in the Script Callback Framework. It provides the GetAsyncCallbackEventReference and GetSyncCallbackEventReference functions, which generate the JavaScript code to be attached to the Control's event.

/// <summary>

/// Obtains a reference to a client-side script function

/// that causes, when invoked, 

/// the server to callback asynchronously to the page. 

/// This method also passes a parameter to the server control

/// that performs the callback processing on the server. 

/// </summary>

/// <param name="control">The server control to process the callback.

///    It must implement the IClientCallbackEventHandler</param>

/// <param name="args">Argument to be passed to the server control

///    that performs the callback processing on the server</param>

/// <param name="cbHandler">Callback Handler (JavaScript function),

///    invokes when callback operation completed successfully</param>

/// <param name="context">Any DHTML reference or extra information

///    to pass to the Callback or Error Handler</param>

/// <param name="errHandler">Error Handler (JavaScript function),

///    invokes when error occurred during the callback operation</param>

/// <returns>Asynchronous callback JavaScript code</returns>

public string GetAsyncCallbackEventReference(
     System.Web.UI.Control control,
     string args,
     string cbHandler,
     string context,
     string errHandler)
/// <summary>

/// Obtains a reference to a client-side script function that causes, 

/// when invoked, the server to callback synchronously to the page. 

/// This method also passes a parameter to the server control

/// that performs the callback processing on the server. 

/// </summary>

/// <param name="control">The server control to process

///    the callback. It must implement the IClientCallbackEventHandler</param>

/// <param name="args">Argument to be passed to the server

///    control that performs the callback processing on the server</param>

/// <param name="cbHandler">Callback Handler (JavaScript function),

///    invokes when callback operation completed successfully</param>

/// <param name="context">Any DHTML reference or extra information

///    to pass to the Callback or Error Handler</param>

/// <param name="errHandler">Error Handler (JavaScript function, 

///    invokes when error occurred during the callback operation</param>

/// <returns>Synchronous callback JavaScript code</returns>

public string GetSyncCallbackEventReference(
    System.Web.UI.Control control,
    string args,
    string cbHandler,
    string context,
    string errHandler)

For example, the following call to GetAsyncCallbackEventReference function, will generate the callback JavaScript code and attaches it to the IncreaseButton's onclick event.

Server Side code

// Callback asynchronously when the button is clicked

IncreaseButton.Attributes["onclick"] = GetAsyncCallbackEventReference
(
Form1,
String.Format("document.getElementById('{0}').value", txtValue.UniqueID),
"IncreaseValueHandler",
String.Format("document.getElementById('{0}')", txtValue.UniqueID),
null
);

Client Side code

<input name="IncreaseButton" id="IncreaseButton" 
 type="button" value="Increase Value"
 onclick="javascript:WebForm_DoAsyncCallback('Form1',
 document.getElementById('txtValue').value,
 IncreaseValueHandler,
 document.getElementById('txtValue'),
 null);" />

PageTemplate overrides the Render method of System.Web.UI.Page class to ensure that ScriptCallback.js file and all the script code necessary to perform the callback is correctly referenced within the page or control before rendering to the client. ScriptCallback.js file contains the client code to initialize the callback infrastructure, opens HTTP connection to specified remote ASP.NET Page for callback processing, as well as invokes the corresponding Callback Handler or Error Handler after receiving the result from the server.

Script callbacks

Whenever the control's event is fired, its embedded JavaScript code invokes the WebForm_DoAsyncCallback or WebForm_DoSyncCallback contained in ScriptCallback.js file to callback to the specified remote ASP.NET Page for processing. Internally, it uses a COM object to issue an HTTP POST or GET command to the specified target URL. This COM object comes with Internet Explorer v5.0 or above, and it is an old acquaintance of many developers:

var xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");

The HTTP verb is GET or POST depending on the size of the data to be sent. If the size exceeds 2KB, a POST command is used. The HTTP request consists of three logical elements: __SCRIPTCALLBACKID, __SCRIPTCALLBACKPARAM, and posted data. The __SCRIPTCALLBACKID value contains the ID of the control to be invoked (the event target parameter), whereas __SCRIPTCALLBACKPARAM carries the input parameter for the server-side stub method.

Server responses

PageTemplate overrides the OnInit method of System.Web.UI.Page class to determine if the current request is Postback or Callback mode. It looks for a __SCRIPTCALLBACKID entry in the Request collection. If so, it sets the public IsCallback property to true and concludes that a callback invocation is being made. In the OnLoad method, it first calls the base.OnLoad method to ensure all the controls including those dynamically created controls are properly initialized. If it's a callback, it then invokes the HandleClientCallback function to further process the request.

Internally, it uses the value obtained from __SCRIPTCALLBACKID entry, which is the ID of the control to be invoked. It looks for the specified control in the Page's Controls collection and checks to see if the referenced control implements the IClientCallbackEventHandler interface (which could be the page itself). If the Control implements the required interface, the PageTemplate invokes the RaiseClientCallbackEvent method on the interface, and prepares the response from the results of the call.

The IClientCallbackEventHandler interface has just one method with the following signature:

string RaiseClientCallbackEvent(string eventArgument);

The eventArgument parameter for the method is retrieved from the Request collection of posted values. The string representation of the input data is contained in an entry named __SCRIPTCALLBACKPARAM. This string can be anything you want and need, including numbers, dates, comma-separated values, XML data or Base64 data, JavaScript and so forth.

PageTemplate uses different status codes to notify client script code about the status of callback processing at server. The status code used here has different meaning than those used in the standard HTTP STATUS_CODE. The table shown below lists down the 4 possible status codes and their meaning:

STATUS DESCRIPTION
200 OK
404 Unable to find the specified control
500 Internal Error (Unknown Error)
501 The specified control does not implement the IClientCallbackEventHandler interface

Note: The status code is appended into a custom HTTP Header entry named "__SCRIPTCALLBACKSTATUS".

Callback handling

Once a callback operation has completed, the client script will be notified to check the status code returned from the custom HTTP Header entry named "__SCRIPTCALLBACKSTATUS". It invokes the registered Callback Handler if the status code is "200". Otherwise, the registered Error Handler is invoked to complete the job.

Using the code

With Script Callback Framework in place, developing a Page that makes use of script callbacks is as easy as the few steps listed below:

  1. Implement the IClientCallbackEventHandler interface on any control, which intends to support the callback. The control can be Page, UserControl as well as any Server Controls.
  2. Write the server-side code in the RaiseClientCallbackEvent method that will be invoked from the client. In doing so, you need to define the data model for the call and decide what information to be exchanged and in what format. The actual data being exchanged must be a string, but the contents of the string can be anything you want and need, including numbers, dates, comma-separated values, XML data or Base64 data, JavaScript and so forth.
  3. Assign a unique ID to every control that supports IClientCallbackEventHandler interface either declaratively (HTML) or programmatically (Code).
  4. Attach the JavaScript code emitted from GetAsyncCallbackEventReference or GetSyncCallbackEventReference function to the control's event. When invoked, initiate and handle the callbacks operation asynchronously or synchronously. Please look at the Limitation section if System.Web.UI.Page or its derivative is used as the callback target.

    Limitation

    System.Web.UI.Page or its derivative cannot be used as the first input parameter for GetAsyncCallbackEventReference or GetSyncCallbackEventReference functions because the system is unable to find out their ID. The workaround is that you should declare a variable of type System.Web.UI.HtmlControls.HtmlForm, and its name must match the Form element's ID used in the Page's HTML source, and use this variable as the first input parameter to the aforementioned functions.

    HTML
    <form id="Form1" method="post" runat="server"></form>
    Code behind
    protected System.Web.UI.HtmlControls.HtmlForm Form1;
    
    IncreaseButton.Attributes["onclick"] = GetSyncCallbackEventReference
    (
        Form1,
        String.Format("document.getElementById('{0}').value", 
        txtValue.UniqueID), "IncreaseValueHandler",
        String.Format("document.getElementById('{0}')", 
        txtValue.UniqueID), null
    );
  5. Write the Callback Handler or Error Handler JavaScript function in the Page's HTML source or in any script file referenced by the Page.

In short, you can use callbacks to update individual elements of a page, such as a Label or a Panel, provide different views of the same data, download additional information on demand, or auto-fill one or more fields. To merge the server-side generated values with the existing page, you typically use the Page's DHTML object model. You give each updateable HTML tag a unique ID and modify its contents using the innerHTML property or any other property and method the DOM supplies.

Last, but not least, I've included a sample, available from the link at the top of this article, that demonstrates the capability of Script Callback Framework.

Points of Interest

Throughout the numerous testing on Script Callback Framework, I found that Microsoft.XMLHTTP COM object caches the result of each call by its requested URL and post data. No connection will be opened to the requested URL if the same requested URL and post data have been used before. However, the numbers of cache items is unknown to me.

Conclusion

Script callbacks allow you to perform callbacks to the server without refreshing the whole page, which gives users the illusion that everything is taking place on the client. Once again, I have presented the Script Callback Framework which is a simplified version of code that will be completely built when ASP.NET 2.0 is released. You might as well get the functionality you want today, and you'll be ahead of the curve in understanding this similar concept in ASP.NET 2.0 tomorrow.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here