Microsoft ASP.NET AJAX Extensions is a framework that brings AJAX-style functionality to the ASP.NET 2.0 platform. Unlike other similar frameworks, it is designed to be part of ASP.NET and therefore seamlessly integrate with the existing platform and application model. ASP.NET AJAX will be fully integrated with the next release of Microsoft Visual Studio (codenamed “Orcas”), and along with LINQ it will be one of the pillars of the future ASP.NET platform.
Right from the start, you should think of ASP.NET AJAX Extensions as a native part of the ASP.NET platform and not just as a bolted-on and more or less unobtrusive external library. In the context of ASP.NET, the role of the AJAX subsystem is fairly clear and defined. It is the ingredient that spices up the whole platform and makes it a high-productivity platform for a new generation of Web applications. With AJAX aboard, you can finally write Web applications that work with Internet Explorer, Firefox, Navigator, and Safari browsers with ubiquitous reach and easy deployment.
ASP.NET AJAX Extensions brings enhancements on both the server and client side of the Web platform. It comes with a rich suite of components and controls to make AJAX coding easy and, more importantly, mostly transparent to ASP.NET developers.
There are two main approaches for building ASP.NET AJAX pages-server-centric and client-centric. In the server-centric model, you incrementally add AJAX-based user interface enrichment to new and existing applications. You keep the core part of your user interface and application logic on the server mostly written with Microsoft Visual Basic .NET or C#. At the same time, your applications benefit from the browser’s capabilities through JavaScript code. Your exposure to JavaScript, though, is minimal if not close to zero. Choosing a server-centric model allows ASP.NET developers to add a good deal of interactivity to Web applications with an extremely short learning curve.
The real power of AJAX, however, is harnessed when you can fully leverage JavaScript and the browser’s DOM, in a client-centric model. In this way, you can provide a significantly richer, more interactive, and more immersive user experience. You can build mash-ups and make the application react to user’s input in a way that closely resembles a desktop application.
In the client-centric model, you need good client-side programming skills. You keep control of everything, trigger server operations, grab results, and refresh the user interface. The bandwidth consumption is minimal, but your efforts are significant; and therefore, so is the likelihood of adding bugs and memory leaks.
In the server-centric model, everything is easier. A special component takes care of most of this burden and accomplishes tasks in a more reliable way at the cost of more bandwidth. In the server-centric model of AJAX, a component acts as the manager of the page and takes care of postbacks and page updates. You don’t write code for that; you just have to add a handful of new server controls to pages and tweak the configuration file.
This is currently the most common way to approach AJAX development in the industry. Virtually any AJAX frameworks out there expose an AJAX manager component and a bunch of settings. In this chapter we’ll take a look at the manager component and configuration file of ASP.NET AJAX Extensions pages.
When you create the project of an AJAX-enabled ASP.NET Web site, everything looks like a classic ASP.NET application at first glance. After a second look, though, you can see that the configuration file contains some changes in the form of new sections and new runtime components. In particular, the runtime components-made-to-measure HTTP modules and HTTP handlers-play a key role in the implementation of ASP.NET AJAX.
In ASP.NET, the web.config file stores application settings that apply to the folder where it is located and to child subfolders. Each application can have a variety of web.config files to apply different settings at different folder levels.
The web.config file is a text file written in accordance with a well-known XML schema. The standard schema file features a built-in number of sections and elements, but new sections can be added to configure custom services and components. As mentioned, ASP.NET AJAX Extensions 1.0 is just an extension to ASP.NET, and it can be easily seen as a new service that requires its own set of extensions to the configuration syntax.
New Configuration Sections
The ASP.NET configuration file has a root element named . A particular configuration file that contains information for a custom service can optionally define new sections. All nonstandard sections used in a configuration file must be declared in the initial section. The following code snippet shows the new sections defined in the root web.config file of an ASP.NET AJAX application:
...
As you can see, everything goes under the
Script references obtained from embedded Web resources are served by the ScriptResource.axd HTTP handler. In ASP.NET AJAX, this handler replaces the old acquaintance WebResource.axd handler-a native component of ASP.NET 2.0. What’s the difference? In addition to serving script references, the ScriptResource.axd handler also appends any localized JavaScript resource types for the file.
As a result, you have no system scripts that are an explicit part of your application. To avoid that, you create a directory structure that roots under a custom folder the following subdirectories:
System.Web.Extensions1.0.61025.0
The first directory name must match the name of the assembly that contains the script. The name of the ASP.NET AJAX Extensions assembly is System.Web.Extensions. The second directory matches the version number of the assembly. You can read this version number from the web.config file.
Now set the ScriptPath property on ScriptManager to, say, JS:
All of a sudden, the MicrosoftAjax.js script file is now referenced as shown here:
Needless to say, your pages will fail if no such script files can be found in the specified directory path.
When you install ASP.NET AJAX Extensions, it copies debug and release versions of all system scripts. You can copy these files into your application’s folder from the installation path of ASP.NET AJAX. The typical path looks like the following:
:
Program FilesMicrosoft ASP.NETASP.NET 2.0 AJAX Extensionsv1.0.61025MicrosoftAjax-LibrarySystem.Web.Extensions1.0.61025.0 Release and debug files live in the same directory. Debug files have the .debug string in front of the .js extension. For example, the debug version of MicrosoftAjax.js is named MicrosoftAjax.debug.js.
Note |
ASP.NET AJAX uses a special naming convention to distinguish between debug and release script files. Given a release script file named script.js, its debug version is expected to be filed as script.debug.js. |
Now, thanks to the effect of the ScriptPath property, the source files will be picked up from the disk. Finally, note that you can still define a local Path attribute on a script reference to override the ScriptPath value. It is also possible to force the use of an assembly for a given script reference by using the IgnoreScriptPath attribute on individual script references.
Resolving and Overriding Script Files
In summary, the ScriptPath property allows you to pick up scripts otherwise located in an assembly from a disk folder. The Path attribute, though, can override the global ScriptPath setting for a particular script reference. An interesting use of this feature is when you override a system script, as shown here:
<asp:ScriptReference Name="MicrosoftAjax.js" Path="~/Scripts/MicrosoftAjax2.js" />
In this case, instead of the original MicrosoftAjax.js, the client receives a customized version saved with a different name.
For each script reference, including the client framework scripts, you can also handle the ResolveScriptReference event and dynamically change the script location. For example, you can redirect the script from a server that is geographically nearer to the user. The following example illustrates how to handle the ResolveScriptReference event to redirect to a different script file:
<asp:ScriptManager runat="server" OnResolveScriptReference="ResolveScript"> ...
The script manager fires the ResolveScriptReference event to allow modifications to script references before they are rendered.
Script Globalization
Globalization is a programming feature that refers to the code ability of supporting multiple cultures. A request processed on the server has a number of ways to get and set the current culture settings. For example, you can use the Culture attribute on the @Page directive, the Culture property on the Page class, or perhaps the section in the web.config file. How can you access the same information on the client from JavaScript?
Defined on the script manager, the Boolean EnableScriptGlobalization property enables the ScriptManager control to generate and emit the client culture information. When the EnableScriptGlobalization property is true, the ScriptManager emits proper script code that sets up aclient global Sys.CultureInfo object that JavaScript classes can consume to display their contents in a culture-based way. The JavaScript snippet is shown next. It consists of a global variable named __cultureInfo that is called back by Microsoft AJAX library classes with localespecific formatting options. The variable is given a value that corresponds to the JSON serialization of a Sys.CultureInfo object. Note that you won’t see any of this code in your page unless you explicitly set a culture for your page. No injection will ever take place for the default, invariant culture.
The following code snippet shows how to consume culture information from the client:
The desired date format is set explicitly-weekday, day, month name, year, and time-but it contains culture-specific information, such as the name of the weekday and month. By default, on an English operating system, you get something like the following:
Friday, 02 February 2007 15:52:43
You get this result regardless of the culture settings you might have set in the ASP.NET page. This is because in the example the output is generated in JavaScript, where you have no access to the culture information. Try the following now:
<%@ Page Language="C#" Culture="it-IT" ... %> ...
When you re-run the page, with the EnableScriptGlobalization flag turned on, you get a different result, as shown in Figure 3-2.
Figure 3-2: Showing culture-specific information in a globalized script
Only a few methods on a few JavaScript objects support globalization. In particular, it will work for the localeFormat method of Date, String, and Number types. Custom JavaScript types, though, can be made global by simply calling into these methods or accepting a Sys.CultureInfo object in their signatures.
Script Localization
Script files can have localizable elements such as text strings for messages and user-interface elements. When the EnableScriptLocalization property is set to true and a UI culture is properly set in the page, the script manager automatically retrieves script files for the current culture, if any.
Unlike globalization, localization is driven by the UICulture attribute in the @Page directive and the UICulture property in the Page class. So in an ASP.NET AJAX page that supports both script globalization and localization, you might have the following directive:
<%@ Page Language="C#" UICulture="it-IT" Culture="it-IT" ... %>
This information is not enough for the ScriptManager to pick up localized scripts, if any. You also need to specify which UI cultures you intend to support for each script reference. You indicate the supported cultures through the ResourceUICultures property on individual script references. The property is a comma-separated string of culture symbols. Here’s an example:
<asp:ScriptReference Path="Person.js" ResourceUICultures="it-IT" />
Note that the ResourceUICultures is ignored if the Path attribute is not specified on the script reference tag.
At this point, if the page requires a script named Person.js and the culture is set to it-IT, the ScriptManager object attempts to retrieve a script file named Person.it-IT.js from the same path.
The ScriptManager control exposes a slew of registration methods you can use for managing individual script blocks, hidden fields, arrays, and expando attributes programmatically. If you’re familiar with ASP.NET 2.0, you’ll likely be reminded of similar registration methods defined on the ClientScriptManager class. What’s the difference between ClientScriptManager and the ASP.NET AJAX ScriptManager?
Registration methods on ScriptManager serve the purpose of partial page rendering. Any array, expando attribute, or hidden field you need to carry back and forth over each partial page update must be registered in advance with the script manager. To register scripts and other resources that are not needed for partial page updates, you use methods of the ClientScriptManager class instead.
With explicit registration, the script manager knows what scripts are going to be present and processed during an asynchronous postback. The alternative would be quite costly and error-prone-parsing the contents of the updatable panel looking for the tag. This technique was implemented in early builds of ASP.NET AJAX and proved to be quite poor. Along with the registration step, some dispose logic is also required so that when an updatable panel is cleared out during a postback, any scripts inside of it can tear down anything they need. The ultimate goal is having all controls working properly when hosted in updatable panels, each with its own required piece of script in place. This is an essential point to know if you plan to write custom controls based on ASP.NET AJAX.
In ASP.NET AJAX, the theme of script-related error handling is mainly two-fold. It is made of the usual techniques and issues related to generic error handling local to the code you write. This means, for instance, handling undefined references, checking error codes, types, parameter count, and return values. As we saw in Chapter 2, the Microsoft AJAX library provides some built-in tools that go beyond the basic set of capabilities offered by the original JavaScript language. In addition, an advanced feature of the ScriptManager control allows you to define debug and release versions of your script files and let it decide which one to load based on your input and runtime conditions (that is, are you really debugging your code?).
Next, there’s a second aspect of script-related error handling that is specific to ASP.NET AJAX; in particular, it refers to partial page rendering. Let’s tackle debug and release versions of script files first.
In general, the main difference between debug and release scripts is that the release scripts remove unnecessary blank characters, comments, trace statements, and assertions. Even though it is common today to download script files, even from popular Web sites, that are filled with comments and blank lines, in an AJAX context where script is no less than essential, every little bit you can save is welcome. So AJAX scripts might come in debug and release versions, and you should be ready to write both for your own JavaScript classes.
The question is, which version should be served to the client? And based on what considerations? The easiest approach is to leave the burden on the developer’s shoulders and ask her to link different files as required by the context.
In ASP.NET AJAX Extensions, the logic to choose between debug and release versions has been entirely incorporated in the ScriptManager control. You tell the ScriptManager about the desired script mode-debug or release-and the script manager infers debug files based on a naming convention. A file named MyScript.debug.js is considered to be the debug version of the file MyScript.js. If the file is subject to localization, the corresponding localized (Italian) debug file is MyScript.debug.it-IT.js. This rule applies to both system and custom script files.
The ScriptMode property of the ScriptManager control determines the global algorithm to use for scripts. Each script reference, though, has its own local mode property so that you can have, say, release versions of the Microsoft Client library system files and debug versions of your custom files.
Note |
The ScriptMode property is totally ignored both on ScriptManager and ScriptReference when the retail attribute of the |
The ScriptMode property on both ScriptManager and ScriptReference accepts values from the ScriptMode enumeration and applies them to all referenced scripts and an individual script, respectively. Feasible values for the ScriptMode property are defined in the ScriptMode enumerated type and are listed in Table 3-6.
Value |
Description |
---|---|
Auto |
The ScriptManager or the ScriptReference class decides which type of script to include. The algorithm produces different results based on the location of the script (static file, assembly) and class that invokes it and the runtime conditions. (I’ll say more about this later.) |
Debug |
The ScriptManager loads the debug version of the script unless a made-to-measure script reference overrides the setting. The ScriptReference loads the debug version of the specified script. |
Inherit |
In a ScriptManager control, the setting is equivalent to Auto. In a ScriptReference control, the value from ScriptManager determines which version of the client script to use. |
Release |
The ScriptManager loads the release version of the script unless a made-to-measure script reference overrides the setting. The ScriptReference loads the release version of the specified script. |
At the end of the look-up algorithm, a file name is determined. If the determined script file doesn’t not exist, an error will be raised.
Let’s see how the script version to load is determined in the script manager when ScriptMode equals Auto or Inherit. The version of client scripts is first determined by looking at value of the debug attribute of the @Page directive. If the page is running in debug mode, debuggable scripts are used. The actual setting, though, might be overridden if a matching entry exists for the script in the
When assigned to the ScriptMode property of a ScriptReference control, the value Auto is equivalent to Release for static files. It is equivalent to Inherit for script references defined in an assembly. For a script reference, Inherit means that the script mode is inherited from the script manager.
The following settings guarantee that whatever you set on script manager is replicated by child references:
<asp:ScriptManager runat="server" ScriptMode="Auto"><asp:ScriptReference path="person.js" ScriptMode="Inherit" />
If a debug script must be loaded, the file person.debug.js must be available. If not, a script error occurs on the client.
Important |
The debug version of the Microsoft AJAX library is around 200 KB; the release version is around 70 KB. Does it mean that all this script will be downloaded (and then cached) on the client? Not exactly. In ASP.NET AJAX Extensions, both release and debug scripts may optionally go through a compression HTTP module, and the release scripts are also “crunched” to reduce size further. As a result, you should have approximately 20 KB for release scripts. |
What if an exception is thrown during the execution of a partial page refresh? The client page receives a pop-up message that replicates the contents of the Message property of the original exception. What can you do about it?
The property AsyncPostBackErrorMessage on the script manager class can be used to edit or replace the default message. You can set the property either declaratively in the page markup or during the ScriptManager’s AsyncPostBackError event. If the value is empty, the exception’s message will be used. Here’s an example:
void AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e) { string msg = "An error occurred and its description is: {0}"; msg = String.Format(msg, e.Exception.Message); ScriptManager1.AsyncPostBackErrorMessage = msg; }
When executed, the client-side error message appears as you see in Figure 3-3.
Figure 3-3: An error occurred during a partial page refresh.
What if you don’t like popups and want to redirect the user to an error page instead? In this case, you configure the page to use the traditional error-handling mechanism for ASP.NET pages. You configure the
...
Alternatively, you can use the global Error event in global.asax and, for example, log the error and programmatically redirect to a custom error. This behavior is fully supported by ASP.NET AJAX and can be disabled by setting to false the value of the AllowCustomErrorRedirects property of the ScriptManager object. When AllowCustomErrorRedirects is set to false, the ScriptManager overrides custom error redirects and instead sends the error to the client, where you can display error information without redirecting the user to another page. On the client, though, you get a pop-up message. Is there a way to show a message instead? You bet.
By adding the following script to the page, you register a handler for the endRequest event of the PageRequestManager client object. (See Chapter 2, “The Microsoft Client Library for AJAX”.)
The endRequest client event fires at the end of a partial page refresh operation. The event handler receives an EndRequestEventArgs class through the args parameter. As we’ll see more clearly in Chapter 4, the EndRequestEventArgs class features two key properties: error and errorHandled.
The property error returns a JavaScript object that represents the server-side exception. From this object, you get the error message. The errorHandled property is a Boolean value that indicates whether the script is done with the error. By setting this property to true, you disable the popup and get output like that shown in Figure 3-4.
Figure 3-4: Incorporating the error message in the page
The ScriptManager control is in some sense the heart and soul behind each ASP.NET AJAX page. It orchestrates partial page refreshes, bootstraps the Microsoft AJAX library by loading scripts, generates proxies for local Web services, and coordinates the work of updatable panels. The ScriptManager control also provides functionality to control developers writing AJAX-enabled controls.
Understanding the behavior of this control is key to setting up effective, no-surprises ASP.NET AJAX pages that load custom script and perform the most common and popular AJAX-style operation-partial page rendering. In the next chapter, we’ll take the plunge into the UpdatePanel control-that is, the brains behind partial page rendering.
Part I - ASP.NET AJAX Building Blocks
Part II - Adding AJAX Capabilities to a Site
Part III - Client-Centric Development