Share via


Cutting Edge

Inside the Microsoft AJAX Library

Dino Esposito

Code download available at:CuttingEdge2007_12.exe(152 KB)

Contents

Introducing the Microsoft AJAX Library
A Class According to the Microsoft AJAX Library
Built-In Classes in the Microsoft AJAX Library
Where's XMLHttpRequest?
Details of a Web Request
Proxy Objects for Script Services
The Partial Rendering Engine

There's an unavoidable connection between AJAX and JavaScript that is implied by the AJAX acronym, which stands for Asynchronous JavaScript and XML. JavaScript, though, is a simple programming language whose foundation was laid out quite a few years ago when object-oriented programming was considered overkill for Web pages. At the same time, principles of dynamic languages were considered too academic to be fully implemented in a broadly used programming language.

As a result, JavaScript contains a bit of everything: it supports some object-oriented semantics in a similar fashion as more fully object-oriented language like C#, and it has a number of dynamic binding features like today's Ruby or Python. JavaScript is, however, the language that zillions of Web pages use to make displayed pages more interactive rather than a static composition of text, tables, and graphics.

What is familiarly called JavaScript is actually the browser's implementation of the standardized ECMAScript language. You use the language primarily to embed functions in HTML pages that glue together a user's actions with elements in the page. The elements in the page—buttons, textboxes, and so on —are modeled after the schema defined in the Document Object Model (DOM). Using JavaScript, you can programmatically rename, edit, add, or delete elements in the displayed document and handle any events fired by such elements. In addition, you can perform any browser-specific action, such as opening or popping up a new window or—what makes AJAX so special—invoke the popular XMLHttpRequest object and place asynchronous calls to a remote URL.

The key fact to bear in mind is that JavaScript and the DOM, by themselves, no longer provide developers with the full programming power needed in the current age of the Web. They represent a kind of minimal toolbox to work with. To craft good AJAX pages, more is needed, such as a rich client-side JavaScript library.

A big benefit of AJAX is the ability to run more of a site's code on the client, thus enabling the browser to respond quickly to user actions. Typical AJAX actions, though, go far beyond simply switching images when the mouse rolls over an element or displaying a pop-up window with a menu and toolbar. AJAX actions include client-side data validation, advanced user interface features such as floating menus, drag and drop, pop-up panels, and sophisticated input. AJAX actions also include dispatching requests to the server in an out-of-band way that bypasses the classic model of replacing the entire page.

Whatever AJAX platform you want to consider must be backed by a rich JavaScript library. The library typically contains at least a ready-to-use set of client-side user interface gadgets and an application model with events that define a lifecycle for each loaded document. In ASP.NET AJAX 1.0 and in the AJAX platform integrated with ASP.NET 3.5, the JavaScript library of choice is the Microsoft® AJAX Library.

Introducing the Microsoft AJAX Library

The Microsoft AJAX Library consists of two related parts: JavaScript language extensions and a sort of base class library to provide predefined services and tools to developers. Although the notion of an object is pervasive, JavaScript by itself can't be presented as an object-oriented language, as it fails to fully and natively implement all of the three pillars of object-oriented programming: inheritance, polymorphism, and encapsulation. There's a bit of inheritance you can get through object prototypes and there's a bit of encapsulation you obtain via closures. So the Microsoft AJAX Library first endows the language with more powerful tools and then proceeds with the definition of new classes and programming tools.

The Microsoft AJAX Library itself is self-contained, written in JavaScript, and stored in a couple of .js files. This means that any programming environment that accepts JavaScript can successfully use the Microsoft AJAX Library provided that the constituent files are properly referenced. There are two main files involved in the Microsoft AJAX Library: MicrosoftAjax.js and MicrosoftAjaxWebForms.js. MicrosoftAjax.js defines language extensions supported by the Microsoft AJAX Library including namespaces, interfaces, enums, and inheritance. MicrosoftAjaxWebForms.js defines the partial rendering engine and the whole network stack.

In an ASP.NET AJAX application, you don't need to reference or embed any of these two files directly. This is one of the tasks that the ScriptManager control accomplishes for you:

<asp:ScriptManager runat="server" ID="ScriptManager1" />

Placed in an ASP.NET AJAX page, the preceding code guarantees that one or both of the Microsoft AJAX Library .js files will be downloaded to the client. If you're going to extend the capabilities of the browser's JavaScript in the context of a non-ASP.NET AJAX application, then you are going to have to reference any required .js files explicitly:

<head> <script src="microsoftajax.js"></script> </head>

A Class According to the Microsoft AJAX Library

In traditional JavaScript programming, you work by creating functions. Because a function is a first-class element, you can create new instances of a function and configure it for execution. Put another way, you use functions much as if they were objects in an object-oriented language. This is a pillar of the JavaScript architecture and can't be changed without the serious risk of breaking many of applications and a large number of existing browsers. The Microsoft AJAX Library performs some tricks and makes some assumptions with the final goal of making JavaScript functions look and behave like classic objects.

You create Microsoft AJAX Library classes using the prototype model. All JavaScript functions feature the prototype property. The prototype property is meant to represent the internal-yet-public schema of the object. All instances of that function will end up calling the same method instance if the method is defined as part of the function's prototype.

As an example, suppose you have a Timer object:

MsdnMag.Timer = new function() { // This is a sort of constructor // for the function : this._interval = 5000; }

According to the Microsoft AJAX Library, this is the definition of a class. As you can see, though, it's a mere function definition. Through the preceding code, you define a function object that can be further instantiated in any page that links that code:

var timer = new MsdnMag.Timer();

Any code defined in the pseudo-constructor executes at this time. According to the preceding snippet, instantiating the function sets the internal variable _interval to 5000.

What should you do to add a couple of start and stop methods to the timer object? The prototype model specifies that you use several separate blocks of code to define a class. In an object-oriented programming language such as C#, all the code of a class is contained in a single class definition (with the obvious exception of partial classes). To add a method to the timer object, you need to add a seemingly unrelated piece of code, like this one:

MsdnMag.Timer = new function() { this._interval = 5000; this._timerID = null; } MsdnMag.T imer.prototype 
.start = function(callback) { this._timerID = window.setTimeout(callback, this._interval); }

Let's take a closer look at this code fragment, as it contains some important clues about Microsoft AJAX Library programming. To start out, compare this code to some equivalent C# code:

namespace MsdnMag { public class Timer { public Timer() { _interval = 5000; _timerID = null; } protected int _interval; protected object _timerID; public void start(Delegate callback) { // Any code that can create a timer _timerID = CreateTimer(callback, _interval); } } }

The first thing you should notice is that you have no need to explicitly define _timerID and _interval as part of the Timer object in the Microsoft AJAX Library. You just assign both members a default value in the constructor. However, to mark them as members of the object, you should prefix them with the "this" keyword. If you don't, they would be considered private members of the function and not bound to the object's prototype. When, prefixed with the "this" keyword, you can invoke those members from any prototyped methods.

The MsdnMag.Timer function, as defined in this column, illustrates the recommended approach for building new classes on top of the Microsoft AJAX Library in ASP.NET AJAX and any other platform where the Microsoft AJAX Library is used. Take a look at Figure 1 and compare it to the preceding code snippets.

Figure 1 Closure for the MsdnMag.Timer Class

// // MsdnMag.Timer :: closure // MsdnMag.Timer = function(interval) { var _interval = interval; var _timerID = null; var _userCallback = null; this.CallbackInternal() { this._userCallback(); this.start(); } this.start(callback) { _userCallback = callback; _timerID = window.setTimeout(CallbackInternal, _interval) } this.stop() { _userCallback = null; _timerID = null; } }

Here, the JavaScript object is written as a closure rather than as a prototyped object. Both models are supported by the JavaScript engine supplied by most browsers. So what's the difference? The closure model delivers an object that has all of its members entirely defined in the same overall context, much like a C# class. The prototype model serves up a class with a sort of distributed scheme. All members attached to the prototype of the object are shared by all instances of the object. The Microsoft AJAX Library is entirely written according to the prototype model and Microsoft strongly recommends that you use that model for your own objects. On the other hand, the Microsoft AJAX Library is just an abstraction layer on top of JavaScript, and JavaScript supports both closures and prototypes. Therefore, the choice between the programming styles is yours.

So what's the benefit of using the Microsoft AJAX Library? The library extends the JavaScript programming environment by adding a number of predefined objects that provide new language features such as namespaces, enumerated types, delegates, stronger typing, and inheritance. Figure 2 shows the MsdnMag.Timer object written as a fully qualified Microsoft AJAX Library object. The sample timer class accepts up to three arguments in the constructor to initialize the interval of the timer and the callback to run periodically. The timer is automatically restarted until explicitly stopped and a reset callback is also provided.

Figure 2 Prototype-Based Version of MsdnMag.Timer

// // MsdnMag.Timer :: prototype model // // Define the namespace the following class(es) belong to Type.registerNamespace("MsdnMag"); // Class constructor:: Initializes internal members // // interval :: interval of the timer in milliseconds // callback :: JS function to execute periodically // resetCallback :: JS function to execute when the timer is stopped MsdnMag.Timer = function(interval, callback, resetCallback) { this._interval = interval; this._timerID = null; this._userCallback = callback; this._resetCallback = resetCallback; // Define a delegate to invoke the user-defined callback this._callback = Function.createDelegate(this, this._callbackInternal); } // Internal wrapper for the user-defined callback function MsdnMag$Timer$_callbackInternal() { // Invoke the user-defined callback if (this._userCallback !== null) this._userCallback(); // Restart the timer if a reset-callback is provided if (typeof(this._resetCallback) !== "undefined" && this._resetCallback !== null) this.start(); } // Official end-point to start the timer function MsdnMag$Timer$start() { this._timerID = window.setTimeout(this._callback, this._interval) } // Official end-point to stop the timer function MsdnMag$Timer$stop() { window.clearTimeout(this._timerID); this._timerID = null; // Execute the user-defined clear function if (typeof(this._resetCallback) !== "undefined" && this._resetCallback !== null) this._resetCallback(); } // Define the public interface of the class MsdnMag.Timer.prototype = { start : MsdnMag$Timer$start, stop : MsdnMag$Timer$stop, _callbackInternal : MsdnMag$Timer$_callbackInternal } // Register the class with the Microsoft AJAX Library framework MsdnMag.Timer.registerClass("MsdnMag.Timer");

Note that when you omit a declared parameter in a JavaScript function call, the actual value of the formal parameter evaluates to an undefined type. For this reason, you should check for both null values and undefined types. The following code shows a safe way to check for null-ness of JavaScript parameters:

if (typeof(this._resetCallback) !== "undefined" && this._resetCallback !== null) { ... }

What if you want to add a public read/write property to a new object? In JavaScript you can't play tricks to make an attribute with a get accessor or a set modifier usable via a property-based programming interface. As long as you want it to be a property, and not just a public data container like a field, you need to read and write property values using ad hoc methods. The convention in use is get_XXX and set_XXX, where XXX is the name of the property. A sample implementation for an Interval property on the MsdnMag.Timer object is shown in Figure 3.

Figure 3 Adding Properties to MsdnMag.Timer

function MsdnMag$Timer$get_Interval() { if (arguments.length !== 0) throw Error.parameterCount(); return this._interval; } function MsdnMag$Timer$set_Interval(value) { var e = Function._validateParams(arguments, [{name: 'value', type: Number}]); if (e) throw e; this._interval = value; } // Define the public interface of the class MsdnMag.Timer.prototype = { get_Interval : MsdnMag$Timer$get_Interval, set_Interval : MsdnMag$Timer$set_Interval, start : MsdnMag$Timer$start, stop : MsdnMag$Timer$stop, _callbackInternal : MsdnMag$Timer$_callbackInternal }

In the get accessor, you check the number of passed arguments and raise a parameter count error if any are specified. Note that "arguments" is an internal array of JavaScript that returns the list of parameters for a function call.

The _validateParams method on the Microsoft AJAX Library Function object is an extension that makes it straightforward for developers to check types and number of method parameters:

var e = Function._validateParams( arguments, [{name: 'value', type: Number}]);

The function takes the argument list and a second array to describe the expected list of arguments. In the preceding code snippet, the method is expected to receive just one numeric argument with the formal name of value. The _validateParams method verifies that the arguments array contains just one numeric element and raises an exception otherwise.

Built-In Classes in the Microsoft AJAX Library

Now that you know how to create new classes in JavaScript using the Microsoft AJAX Library framework as a basis, let's explore the system classes built into the framework. Figure 4 describes all the JavaScript classes defined in the Microsoft AJAX Library. The intrinsic JavaScript objects such as Boolean, Date, and Number are extended to incorporate new methods and capabilities. For example, the Date object now includes a couple of new instance methods named localeFormat and format. You can use them to render a date using the locale culture information:

Figure 4 Microsoft AJAX Library Built-In Classes

Intrinsics Description
Array Extends the native Array object with new search methods.
Boolean Extends the native Boolean object with a parse method.
Date Extends the native Date object with formatting methods.
Error Extends the native Error object to make it similar to a managed exception object. Also exposes static properties to map predefined types of error.
Function Extends the native Function object with delegates and utilities to check method signatures.
Object Extends the native Object object with type information.
Number Extends the native Number object with parsing and formatting methods.
RegExp Simple wrapper around the native RegExp object.
String Extends the native String object with formatting methods.
Type Alias for Function where all OOP extensions are grouped.
Sys.UI Namespace Description
Sys.UI.Behavior Defines the foundation of behaviors used to extend the capabilities of new and existing ASP.NET server controls.
Sys.UI.Bounds Defines a region in the page through top-left-bottom-right parameters.
Sys.UI.Control Defines the foundation of Microsoft AJAX Library user interface controls.
Sys.UI.DomElement Wrapper class for a DOM element rendered in the page.
Sys.UI.DomEvent Wrapper class for a DOM-level event (such as keystrokes or mouse movements).
Sys.UI.Point Defines a point in the page through (x,y) coordinates.
Foundation Classes Description
Sys.Component Defines the foundation of a Microsoft AJAX Library user interface component.
Sys.CultureInfo Defines culture information.
Sys._Application Internal class representing the lifecycle of the current page.
Sys._Debug Internal class offering debug services.
Sys._ScriptLoader Internal class taking care of loading external scripts.
Network Classes Description
Sys.Net.NetworkRequestEventArgs Defines the data associated with the Web request events.
Sys.Net._WebRequestManager Internal class representing the centralized manager of Web requests used to set global parameters such as executor and timeout.
Sys.Net.WebRequest Represents a Web request being made.
Sys.Net.WebRequestExecutor Represents the object in charge of physically executing a Web request.
Sys.Net.WebServiceError Wrapper class for any exceptions occurred during a service call.
Sys.Net.WebServiceProxy Defines the foundation for JavaScript proxy classes of remote services.
Sys.Net.XMLHttpExecutor Represents the Web request executor that uses XMLHttpRequest.
Service Helper Classes Description
Sys.Services._AuthenticationService Internal wrapper class for the server-side authentication Web service.
Sys.Services._ProfileService Internal wrapper class for the server-side user profile Web service.
Sys.Services._RoleService Internal wrapper class for the server-side role Web service. This class is only available in ASP.NET 3.5.
Sys.Services.ProfileGroup Used to contain the information about profile groups.
Event Data Classes Description
Sys.ApplicationLoadEventArgs Defines the data associated with the pageLoad event.
Sys.CancelEventArgs Defines the foundation for the data object of any interruptible event.
Sys.EventArgs Defines the foundation for the data object of any event.
Sys.EventHandlerList Helper class used to gather all events associated with the execution of Web requests.
Sys.PropertyChangedEventArgs Defines the data associated with the property-changed event of Microsoft AJAX Library components.
Utilities Description
Sys.Serialization.JavaScriptSerializer Helper class used to deserialize ad hoc data inserted in the page and consumed by the Sys.Cul­tureInfo object.
Sys.StringBuilder Used to concatenate strings, the object works in much the same way as the managed StringBuilder class.
Partial Rendering Classes Description
Sys.WebForms.PageRequestManager Root object orchestrating any partial rendering requests and operation.
Sys.WebForms.PageLoadingEventArgs Defines the data associated with the pageLoading event.
Sys.WebForms.PageLoadedEventArgs Defines the data associated with the pageLoaded event.
Sys.WebForms.InitializeRequestEventArgs Defines the data associated with the initializeRequest event.
Sys.WebForms.EndRequestEventArgs Defines the data associated with the endRequest event.
Sys.WebForms.BeginRequestEventArgs Defines the data associated with the beginRequest event.
Sys.UI._UpdateProgress Client-side class to perform update progress operations.
Sys.UI._UpdateProgress Client-side class to perform update progress operations.
var d = new Date(); d.localeFormat("dddd, dd MMMM yyyy, hh:mm:ss");

You can grab information about the current locale using the following code:

var localShortDate = Sys.CultureInfo.CurrentCulture.dateTimeFormat.ShortDatePattern; var d = new Date(); d.localeFormat(localShortDate);

In particular, this code snippet retrieves the short date pattern according to the current culture and uses that to format a date.

Where does the Sys.CultureInfo object retrieve locale information? Information relating to the current culture is emitted in the client page whenever the EnableGlobalization property is set to true on the script manager:

<asp:ScriptManager ID="ScriptManager1" runat="server" EnableScriptGlobalization="true" />

The script manager writes out a piece of JavaScript code that is then parsed and integrated with the Microsoft AJAX Library runtime using the JavaScriptSerializer class. The Date object also features culture-specific methods to try to build a new date object from arbitrary text.

The Number object has similar capabilities for culture-specific parsing and formatting.

The Boolean object counts an additional static parse method that recognizes words like "true" and "false".

Array and String are other intrinsic types that received significant extensions in the Microsoft AJAX Library. In particular, the Array type features a few new static methods to add, remove, search, and iterate through items. The String type has been made as close as possible to the managed String class by adding methods such as endsWith, startsWith, trimming, formatting, and integration with string builders.

All objects now return precise type information. Whatever object you query for, its type method is now capable of telling you the exact type. Here's how to proceed:

myTimer = new MsdnMag.Timer(2000, updateUI, timerReset); alert(Object.getTypeName(myTimer));

Figure 5 shows the effect of the alert function.

Figure 5 Checking Type Information with AJAX

Figure 5** Checking Type Information with AJAX **(Click the image for a larger view)

Of all the native JavaScript objects, only RegExp, the object behind built-in support for regular expressions, is not extended in any way in the Microsoft AJAX Library. Once the library is installed, the RegExp object you get to use is exactly the same as before the Microsoft AJAX Library.

Where's XMLHttpRequest?

Although primarily designed to provide a richer and more object-oriented JavaScript programming environment, the Microsoft AJAX Library is primarily perceived as an AJAX library. So a good question would be, where's XMLHttpRequest? If you take a closer look at the Microsoft AJAX Library class hierarchy, you'll see that XMLHttpRequest is buried in the network stack in the classes of the Sys.Net namespace.

In the Microsoft AJAX Library, all Web requests pass through a Web request manager object. A unique instance of the Sys.Net_WebRequestManager object is created upon loading and used for the lifetime of the page. Requests are fired asynchronously, but the manager can't accept more than one request at a time. It's unlikely in a Web page, but should the same instance of the request manager be asked to serve requests simultaneously, then an exception would be raised for the second request. It's the responsibility of the developer to plan a second attempt at a later time.

The Web request manager maintains a list of event handlers and defines the timeout and executor type for requests. You set the default timeout through the defaultTimeout property and you can change the default executor type using the defaultExecutorType property, a string. By default, the executor is an instance of the Sys.Net.XMLHttpExecutor class and is instantiated just before firing the request.

The executor is the code that physically connects to the remote URL and sends the HTTP packet. In AJAX, the most obvious way of accomplishing this is via the browser's XMLHttpRequest object. The Sys.Net.XMLHttpExecutor class just wraps the XMLHttpRequest object and uses its services to send an HTTP packet to its destination.

As you can see, the executor is a separate module and the manager uses it only through a known interface made of the executeRequest method and the invokingRequest and completedRequest events. Let's delve deeper into the mechanics of Web requests in the Microsoft AJAX Library.

Details of a Web Request

The Sys.Net.WebRequest class defines the HTTP packet sent to a server URL for processing. The public properties of the object are url, httpVerb, headers, body, timeout, userContext, and executor. Timeout and executor, if not set, default to the values defined in the Web request manager. The completed event and the invoke method complete the programming interface of the WebRequest object. To trigger a request, you call the invoke method. As you can see, only one request at a time is supported and it is passed on to the request manager for execution:

function Sys$Net$WebRequest$invoke() { if (arguments.length !== 0) throw Error.parameterCount(); if (this._invokeCalled) { throw Error.invalidOperation(Sys.Res.invokeCalledTwice); } Sys.Net.WebRequestManager.executeRequest(this); this._invokeCalled = true; }

The request manager checks to see whether there is an explicit demand for an executor in the packet. If not, it uses the default executor. Next, the request manager fires the invokingRequest method. If the event is not canceled by the user code, the final step takes place: the executor physically sets up the connection and sends the packet.

The executor derives from a base class named WebRequestExecutor, which defines the expected programming interface. As mentioned, the default executor is XMLHttpExecutor. This class internally creates an instance of the XMLHttpRequest browser object and uses it to carry the request.

A different executor can be specified for each Web request, as it is a property of the Sys.Net.WebRequest object. Only one actual executor is defined in the Microsoft AJAX Library—XMLHttpExecutor—but others could be created by developers as well. The point is, what other techniques and alternatives exist to justify the creation of a custom executor?

An additional executor could use a dynamically created, invisible IFRAME to download any response sent back by any URL. You should notice that, when using XMLHttpRequest, you're limited to the so-called same-origin-policy that most browsers implement. In practice, it means that browsers do not allow scripted calls to URLs located outside the domain of the current page. When using XMLHttpRequest, you can't call into, say, contoso1.com from contoso2.com. You can accomplish this, instead, using an IFRAME to grab the response and then parse it to any suitable JavaScript object.

To write an IFRAME-based executor, you should start with a Microsoft AJAX Library object that inherits from Sys.Net.WebRequestExecutor and implements all of its abstract members. As for parsing the response into a usable memory object, you might want to define a request-specific parser component that provides a custom behavior under a common programming interface.

Proxy Objects for Script Services

Scriptable services need some JavaScript wrapper code in order to be invoked. A script-based proxy class can therefore be generated that provides a public programming interface that is nearly identical to that of the original service—be it a classic ASP.NET Web service or a Windows Communication Foundation (WCF) service. The proxy class is linked to the page via a script tag that is bound to a specific URL:

https://www.yourserver.com/service.asmx/js

The name of the proxy object is the same as the fully qualified name of the service class. The object also has the same set of methods. Methods, though, have an extended signature that encompasses additional parameters in addition to the standard set of input arguments. In particular, each method adds two callback functions to allow user code the ability to handle a successful or failed call, plus an optional object that represents the context of the call. The proxy object derives from the Sys.Net.WebServiceProxy Microsoft AJAX Library class.

Figure 6 shows a fragment of a service proxy class. For performance reasons, a proxy class is a singleton that executes each of its methods asynchronously in a callback model. In this code, the _invoke method ends up creating a Web request and—not a minor point—sets the content type to application/json, a required setting for the ASP.NET AJAX runtime. Here's a sample fragment:

Figure 6 Web Service Proxy Class

Type.registerNamespace('MsdnMag'); MsdnMag.SimpleService = function() { MsdnMag.SimpleService.initializeBase(this); this._timeout = 0; this._userContext = null; this._succeeded = null; this._failed = null; } MsdnMag.SimpleService.prototype = { DoSomething : function(succeededCallback, failedCallback, userContext) { return this._invoke(MsdnMag.SimpleService.get_path(), 'DoSomething', false, {}, succeededCallback, failedCallback, userContext); } } MsdnMag.SimpleService.registerClass( 'MsdnMag.SimpleService', Sys.Net.WebServiceProxy); MsdnMag.SimpleService._staticInstance = new MsdnMag.SimpleService(); MsdnMag.SimpleService.DoSomething = function(onSuccess,onFailed,userContext) { MsdnMag.SimpleService._staticInstance.DoSomething( onSuccess, onFailed, userContext); }

var request = new Sys.Net.WebRequest(); request.get_headers()['Content-Type'] = 'application/json; charset=utf-8';

A timeout is automatically set and controlled through an internal timer. Furthermore, the proxy registers a handler for the completed event of the Web request. When the event fires, the system-provided code checks the response. If all went well, the proxy invokes the user-defined success callback. Otherwise, it invokes the failure callback and passes it information about the error that occurred, as in this code snippet:

var error = response.getResponseHeader("jsonerror"); var errorObj = (error === "true"); if (errorObj) err = new Sys.Net.WebServiceError(false, result.Message, result.StackTrace, result.ExceptionType); ... onFailure(err, userContext, methodName);

If the execution of the service method results in an unhandled exception, the message that is associated with the exception, its stacktrace, and its type are carried back to the client and composed into a WebServiceError JavaScript object that the failure callback can work with.

The Partial Rendering Engine

The key object for partial rendering is PageRequestManager, in the Sys.WebForms namespace. The code that initializes the object is transparently inserted in any client page that includes at least one UpdatePanel control. If you view the source of such a page, you'll find a piece of script like the following:

Sys.WebForms.PageRequestManager._initialize( 'ScriptManager1', document.getElementById('form1'));

The ID of the ScriptManager control as well as the ID of the form are arbitrary. The _initialize method on the PageRequestManager does two main things. First, it creates the unique instance of the object to serve all needs in the page lifecycle. Second, it registers a script handler for the form's submit event. In this way, you end up having a common piece of code to intercept any type of postback—regular postbacks done via the browser, cross-page postbacks, and postbacks commanded through the DOM.

The goal of the submit handler is stopping the browser's default action and replacing the form submission with an AJAX request managed through the default Web request executor.

The submit handler starts its job by composing the body of the request. It does that looping through the input fields in the form and creating a proper string. The body is nearly identical to the body the browser would generate itself except for an extra parameter:

ScriptManager1=UpdatePanel1

Again, the IDs here are arbitrary. What matters is that you assign to the ID of the page's script manager the ID of the updatable panel triggering the call.

Once the request is ready, the PageRequestManager first fires the client-side initializeRequest event. The event gives developers a chance to cancel the operation. If the operation can proceed, the manager kills any pending operation—only one partial rendering operation can be active at a time—and fires the beginRequest event to notify that the request is going to leave. Next, it calls invoke on the corresponding WebRequest object. When the response is back, it is parsed to look for errors and the pageLoading event is fired if all is fine.

When the pageLoading event arrives, the response has been parsed but not yet merged with the current DOM of the page. Next, the panels involved with the operation are refreshed and any other data coming from the server is processed, including data items and postback scripts. Finally, the pageLoaded event is fired followed by the endRequest event. Each PageRequestManager event has its own class and carries specific information.

Send your questions and comments for Dino to cutting@microsoft.com.

Dino Esposito is a mentor at Solid Quality Learning and the author of Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005). Based in Italy, Dino is a frequent speaker at industry events worldwide. You can get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.