Chapter 3. The Pulsing Heart of ASP.NET AJAX

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.

Configuration of ASP.NET AJAX

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.

The web.config File

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 <configuration>. 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 <configSections> section. The following code snippet shows the new sections defined in the root web.config file of an ASP.NET AJAX application:

<configuration>
  <configSections>
    <sectionGroup name="system.web.extensions" ...>
      <sectionGroup name="scripting" ... />
        <section name="scriptResourceHandler" ... />
        <sectionGroup name="webServices" ... >
           <section name="jsonSerialization" ... />
           <section name="profileService" ... />
           <section name="authenticationService" ... />
        </sectionGroup>
      </sectionGroup>
    </sectionGroup>
  </configSections>
  ...
</configuration>

As you can see, everything goes under the <scripting> section which, in turn, is a child of <system.web.extensions>. Under the <scripting> section, you find another child section named <webServices>. Let’s dig out the <scripting> section first.

Note

Note

Sections are a necessary syntax element in configuration files. However, you don’t need to declare all sections in all application-specific web.config files. When processing a web.config file, in fact, ASP.NET builds a configuration tree starting from the root machine.config file. Because all standard sections are already declared in the machine.config that ships with the .NET Framework, your application needs to declare only custom sections you plan to use.

Because ASP.NET AJAX Extensions 1.0 is an extension to ASP.NET, new and specific sections are to be declared in the web.config file of each AJAX-enabled application. To avoid that, you can define new sections in the machine.config file located in the %Windows%Microsoft.NETFrameworkv2.0.50727Config folder of the Web server machine.

The <scripting> Section

The <scripting> section might contain a child section named <scriptResourceHandler>. The child section determines how ASP.NET AJAX system scripts should be served to the client and handled. The <scriptResourceHandler> section features two attributes: enableCaching and enableCompression. Both are Boolean values and are set to true by default:

<scripting>
   <scriptResourceHandler enableCaching="true" enableCompression="true" />
</scripting>

The enableCaching attribute indicates whether the system script files (as well as any other linked scripts and resources) are to be cached locally or downloaded for each request. System script files are a key element of AJAX frameworks, and ASP.NET AJAX Extensions is no exception. The more the framework is rich with features, the more the size of such script files becomes large.

To keep the overall size of system script files below the threshold of a few KBs, compression is required. You can enable or disable compression through the enableCompression attribute. When the attribute is turned on, script files that are embedded as resources in an assembly and string resources are compressed.

Warning

Warning

Compression and caching applies only to scripts and resources served by the ASP.NET AJAX script resource handler—ScriptResource.axd. To use the handler, you have to place scripts and resources in an assembly and link them to the ScriptManager control. Just linking a .js file through a <script> tag doesn’t afford you the benefits of caching and compression. When static, file-based scripts are referenced, you will need to provide compression through IIS or through some other compression utility. (For more information, take a look at the following: http://support.microsoft.com/default.aspx/kb/322603.) Note also that the script resource handler doesn’t apply compression and caching to scripts and resources for versions of Internet Explorer older than 7.0. The reason is explained in this article: http://support.microsoft.com/kb/321722.

The <webServices> Section

Under the <webServices> section, you find a number of child sections, as detailed in Table 3-1.

Table 3-1. Children of the <webServices> Section

Section

Description

authenticationService

Configures the ASP.NET AJAX authentication service. With this service active, you can authenticate the user credentials without being redirected to a login page. Authentication can optionally be configured to occur only on secure channels. We’ll return to this topic in Chapter 6, “Built-in Application Services.”

jsonSerialization

Defines the list of custom JSON serializers registered to work in the application. Serializers are grouped under the child <converters> section. The maxJsonLength attribute indicates maximum length in bytes for a JSON string. The default value is 500.

profileService

Configures the ASP.NET AJAX profile service. With this service active, you can manipulate the properties of the ASP.NET user profile from JavaScript. To allow profile properties to be retrieved and modified from within ASP.NET AJAX applications, you add each property name to the readAccessProperties and writeAccessProperties attributes of the section. We’ll return to this topic in Chapter 6.

The <webServices> section is not necessary if you don’t plan to use Web services from within ASP.NET AJAX pages.

The Runtime Engine

As an extension to ASP.NET, ASP.NET AJAX Extensions can’t help but rely on the extensibility model of ASP.NET. Some of its key functionalities are implemented using additional runtime components such as HTTP modules and handlers. When these components are required, you register them in the web.config file, as shown in the following sections.

The ScriptResource HTTP Handler

ASP.NET AJAX needs to register an HTTP handler to effectively manage any system and user-defined script that is being added to each page. The ASP.NET AJAX script manager injects the Microsoft AJAX library and other scripts via a made-to-measure handler named ScriptResource.axd. It is defined as follows:

<httpHandlers>
   <add verb="GET,HEAD" path="ScriptResource.axd" validate="false"
        type="System.Web.Handlers.ScriptResourceHandler,
              System.Web.Extensions, ..." />
</httpHandlers>

If the validate attribute is false, ASP.NET does not attempt to load the specified handler class until an actual request comes for ScriptResource.axd. This behavior potentially delays any runtime errors, but it improves the startup time. Basically, the script resource handler works on JavaScript files and Web resources (images, strings) embedded into assemblies and optimizes the way in which those are injected into your client pages. The handler takes care of serving scripts efficiently for you. For example, it ensures that scripts are cached by the browser and compressed using GZip. Furthermore, it helps you with localization by reading the right version of the resource based on the current culture.

In addition, ASP.NET AJAX might require a second HTTP handler. The configuration code is shown here:

<httpHandlers>
   <remove verb="*" path="*.asmx" />
   <add verb="*" path="*.asmx" validate="false"
        type="System.Web.Script.Services.ScriptHandlerFactory,
              System.Web.Extensions, ..."/>
</httpHandlers>

As you can see, the default handler for ASP.NET Web services is removed (*.asmx files) and replaced with an AJAX-specific handler that falls back to the standard one in some cases. In particular, the new handler for *.asmx resources examines the ongoing HTTP request and lets it go as usual if it can’t find anything in the request that qualifies it as an ASP.NET AJAX request. You don’t need this handler if you’re not going to use ASP.NET Web services from within ASP.NET AJAX client pages.

The ScriptModule HTTP Module

The HTTP module named ScriptModule serves the purpose of executing remote page method calls in JavaScript from within client pages:

<httpModules>
  <add name="ScriptModule"
       type="System.Web.Handlers.ScriptModule, System.Web.Extensions, ..." />
</httpModules>

Without this module, you can’t invoke a properly decorated method on the ASP.NET code-behind class. (We’ll return on this in Chapter 7, “Remote Method Calls with ASP.NET AJAX”.) Of course, if you don’t plan to do so, you don’t need register the ScriptModule HTTP module. This speeds up request processing slightly since the request isn’t routed through this module.

The Script Manager Component

Virtually any AJAX library needs a component or a tool that makes some JavaScript code available on the client. This script code represents the client infrastructure necessary for the library to work. An AJAX library, on the other hand, can’t be entirely based on server-side code and does need a lot of script code. The point is, which component or tool is ultimately responsible for injecting this code?

Had AJAX come along ten years ago, the answer would have been quite obvious: the developer is in charge of linking the client page to each script resource that proves necessary. This approach requires the developer to know quite a bit about the script, including location, name, and functionality.

Today, in the age of codeless and declarative programming, AJAX libraries tend to offer a server-side, often parameterless, component that when dropped on the page automatically outputs any required scripts. This component is generally known as the AJAX manager or script manager. The script manager accomplishes a number of tasks. In addition to ensuring that the proper script is linked to the page, the script manager typically enables and supports partial page rendering, generates proxy code to call remotely into server-side methods and objects, and sets up auxiliary services.

In ASP.NET AJAX, the script manager component takes the form of the ScriptManager server control.

The ASP.NET ScriptManager Control

The main control in the server infrastructure of ASP.NET AJAX Extensions is the ScriptManager control and its twin ScriptManagerProxy control. You will find just one instance of the ScriptManager control in each ASP.NET AJAX page. No AJAX capabilities can be enabled in ASP.NET pages that lack a reference to one ScriptManager control. The ScriptManagerProxy control is used only in master pages scenarios.

The ScriptManager control manages and delivers script resources, thus enabling client scripts to make use of the JavaScript type system extensions and other JavaScript features we covered in Chapter 2, “The Microsoft Client Library for AJAX.” The ScriptManager control also supports features such as partial page rendering and Web service calls.

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

The preceding code shows how to insert the script manager in an ASP.NET page. The control produces no user interface, works exclusively on the server, and doesn’t add extra bytes to the page download.

Properties of the ScriptManager Control

The ScriptManager control features a number of properties for you to configure its expected behavior. Table 3-2 details the supported properties.

Table 3-2. Properties of ScriptManager

Property

Description

AllowCustomErrorsRedirect

Indicates whether custom error redirects will occur during an asynchronous postback. The property is set to true by default.

AsyncPostBackErrorMessage

Gets and sets the error message to be sent to the client when an unhandled exception occurs on the server during an asynchronous postback. If not set, the native exception’s message will be used.

AsyncPostBackSourceElementID

Gets the ID of the server control that triggered the asynchronous postback. If there’s no ongoing asynchronous postback, the property is set to the empty string.

AsyncPostBackTimeout

Gets and sets the timeout period in seconds for asynchronous postbacks. A value of zero indicates no timeout. The property is set to 90 by default.

AuthenticationService

Gets an object through which you can set preferences for the client-side authentication service.

EnablePageMethods

Indicates whether static page methods on an ASP.NET page can be called from client script. The property is set to false by default.

EnablePartialRendering

Indicates whether partial rendering is enabled for the page. The property is set to true by default.

EnableScriptGlobalization

Indicates whether the ScriptManager control renders script in the client that supports parsing and formatting of culture-specific information. The property is set to false by default.

EnableScriptLocalization

Indicates whether the ScriptManager control retrieves script files for the current culture, if they exist. The property is set to false by default.

IsDebuggingEnabled

Indicates whether the debug versions of client script libraries will be rendered. The debug attribute on the @Page directive doesn’t affect this property.

IsInAsyncPostBack

Indicates whether the current page request is due to an asynchronous postback.

LoadScriptsBeforeUI

Indicates whether scripts are loaded before or after markup for the page UI is loaded.

ProfileService

Gets an object through which you can set preferences for the client-side profile service.

ScriptMode

Gets and sets the type (debug or retail) of scripts to load when more than one type is available. Possible values come from the ScriptMode enumeration type: Auto, Inherit, Debug, or Release. The default value is Auto, meaning that the type of script is determined on the fly.

ScriptPath

Indicates that scripts should be loaded from this path instead of from assembly Web resources.

Scripts

Gets a collection of script references that the ScriptManager control should include in the page.

Services

Gets a collection of service references that the ScriptManager control should include in the page.

SupportsPartialRendering

Indicates whether a particular browser or browser version can support partial page rendering. If this property is set to false, regardless of the value of the EnablePartialRendering property, no partial rendering will be supported on the page. The property is set to true by default.

Some properties in the table refer to features that we’ll cover in future chapters, such as partial page rendering and asynchronous postbacks (Chapter 4, “Partial Page Rendering”) or client-side services (Chapter 6). Other properties, on the other hand, refer to the core functionalities of the script manager and will be further discussed in a moment.

Figure 3-1 shows the message box you receive if an asynchronous postback takes too much time to complete. (See the entry for the AsyncPostBackTimeout in Table 3-2.)

The timeout message box for asynchronous requests that time out

Figure 3-1. The timeout message box for asynchronous requests that time out

You get the error shown in Figure 3-1 if, for example, you set a breakpoint somewhere in the server code and then hang out for a couple of minutes before stepping out of the debugger.

Methods of the ScriptManager Control

Table 3-3 lists the methods defined on the ScriptManager control.

Table 3-3. Methods of ScriptManager

Method

Description

GetCurrent

Static method, returns the instance of the ScriptManager control active on the current page.

RegisterArrayDeclaration

Static method, ensures that an ECMAScript array is emitted in a partial rendering page.

RegisterAsyncPostBackControl

Takes note that the specified control can trigger an asynchronous postback event from within an updatable panel.

RegisterClientScriptBlock

Static method, ensures that the specified script is emitted in a partial rendering page.

RegisterClientScriptInclude

Static method, ensures that the markup to import an external script file through the src attribute of the <script> tag is emitted in a partial rendering page.

RegisterClientScriptResource

Static method, ensures that the markup to import an external script from the page’s resources is emitted in a partial rendering page.

RegisterDataItem

Registers a string of data that will be sent to the client along with the output of a partially rendered page.

RegisterDispose

Registers controls that require a client script to run at the end of an asynchronous postback to dispose of client resources.

RegisterExpandoAttribute

Static method, ensures that the markup to import a custom, nonstandard attribute is emitted in a partial rendering page.

RegisterExtenderControl

Registers an extender control with the current ASP.NET AJAX page.

RegisterHiddenField

Static method, ensures that the specified hidden field is emitted in a partial rendering page.

RegisterOnSubmitStatement

Static method, ensures that that client-side script associated with the form’s OnSubmit event is emitted in a partial rendering page.

RegisterPostBackControl

Takes note that the specified control can trigger a full postback event from within an updatable panel.

RegisterScriptControl

Registers a script control with the current ASP.NET AJAX page.

RegisterScriptDescriptors

Registers a script descriptor with the current ASP.NET AJAX page.

RegisterStartupScript

Static method, ensures that client-side script is emitted at the end of the <form> tag in a partial rendering page. In this way, the script will execute as the page refresh is completed.

SetFocus

Allows you to move the input focus to the specified client element after an asynchronous postback.

All static methods emit some form of script and markup in the client page. These static methods are the AJAX counterpart of similar methods defined on the page’s ClientScript object that you should know from ASP.NET 2.0. The static RegisterXXX methods on the ScriptManager class ensure that the given piece of script and markup is properly emitted only once in each partial update of the ASP.NET AJAX page.

Similarly, other nonstatic RegisterXXX methods should be seen as tools to emit proper script code in ASP.NET AJAX pages—especially script code that is associated with custom controls. We’ll return to script loading in a moment.

Events of the ScriptManager Control

Table 3-4 details the two events fired by the ScriptManager control.

Table 3-4. Events of ScriptManager

Event

Description

AsyncPostBackError

Occurs when an exception goes unhandled on the server during an asynchronous postback.

ResolveScriptReference

Occurs when the ScriptManager control is going to resolve a script reference.

Both events are much more than mere notifications of something that has happened on the server. Both give you good chances to intervene effectively in the course of the application. For example, by handling the AsyncPostBackError event, you can edit the error message being returned to the client. Furthermore, by handling the ResolveScriptReference event, you can change the location from where the script is going to be downloaded on the client.

The ScriptManagerProxy Control

Only one instance of the ScriptManager control can be added to an ASP.NET AJAX page. However, there are two ways in which you can do this. You can add it directly on the page using the <asp:ScriptManager> tag or indirectly by importing a component that already contains a script manager. Typically, you can accomplish the second alternative by importing a user control, creating a content page for a master page, or authoring a nested master page.

What if a content page needs to add a new script reference to the manager? In this case, you need a reference to the script manager. Although it’s defined in the master page (or in a user control), the script manager might not be publicly exposed to the content page. You can use the static method GetCurrent on the class ScriptManager to get the right instance:

// Retrieve the instance of the ScriptManager active on the page
sm = ScriptManager.GetCurrent(this.Page);

The ScriptManagerProxy class saves you from this sort of coding. In general, in cases where you need features of the ScriptManager control but lack a direct reference to it, you can instead include a ScriptManagerProxy control in the content page.

You can’t have two script managers in the context of the same page; however, you can have a script manager and a proxy to retrieve it. The ScriptManagerProxy control enables you to add scripts and services to nested components, and it enables partial page updates in user controls and nested master pages. When you use the proxy, the Scripts and Services collections on the ScriptManager and ScriptManagerProxy controls are merged at runtime.

Note

Note

The ScriptManagerProxy class is a very simple wrapper around the GetCurrent method of the ScriptManager class, and its programming interface is not an exact clone of the ScriptManager. From within the proxy, you have access only to a limited number of properties, including Scripts, Services, AuthenticationService, and ProfileService. If you need to modify anything else, refer to the GetCurrent static method of the ScriptManager class.

Script Loading

By extensively relying on client capabilities, ASP.NET AJAX requires a lot of script code. The framework itself links a lot of code, as do custom controls and actual user pages. It turns out that registering and loading script and resources—such as hidden fields, styles, and arrays—is a key factor. The ScriptManager control manages resources created to be used by all controls participating in partial page updates.

You add script files using the Scripts collection, either declaratively or programmatically.

Adding Script Resources

The ScriptManager control automatically emits in the client page any ASP.NET AJAX required script. As a page developer, you don’t have to worry about the script code necessary to support JavaScript extensions, the UpdatePanel, or perhaps the Timer control. In addition, you have the Scripts collection available as a tool to register optional and custom scripts. The Scripts collection can be populated programmatically or declaratively through the <asp:ScriptReference> element.

The following example illustrates the script loading model you can use to load optional scripts:

<asp:ScriptManager runat="server" ID="ScriptManager1">
  <Scripts>
    <asp:ScriptReference
         Name="Microsoft.Web.Resources.ScriptLibrary.PreviewGlitz.js"
         Assembly="Microsoft.Web.Preview" />
    <asp:ScriptReference
         Path="~/Scripts/MyLib.js" />
  </Scripts>
</asp:ScriptManager>

You can reference scripts from an in-memory assembly or from a disk file. The second option is faster, as no extra effort is required to extract scripts from the assembly’s resources.

Table 3-5 lists the properties of the ScriptReference class by means of which you can control the loading of scripts.

Table 3-5. Properties of ScriptReference

Property

Description

Assembly

Indicates the assembly that contains in its resources the script to download on the client.

IgnoreScriptPath

Boolean value, indicates whether the ScriptPath value optionally set at the top ScriptManager level has to be ignored. This property is set to false by default.

Name

Name of the script to download on the client.

NotifyScriptLoaded

Boolean value, indicates whether the script resource loader should automatically append a script loaded notification statement to let the Sys.Application object know when the script is loaded. This property is set to true by default.

Path

Indicates the server path where the script to download on the client can be found.

ResourceUICultures

A comma-delimited string of valid user-interface (UI) cultures supported by the path.

ScriptMode

Specifies the algorithm for choosing between the debug and release versions of the script file. If no debug version exists, the ScriptReference class automatically falls back to release code. Feasible values for the property come from the ScriptMode enumeration type.

Mapping Script Paths

You can reference script files, including ASP.NET AJAX system scripts, either from an assembly or from a disk file. There are a couple of benefits of using disk files. First, you gain something in performance because less work is required to download. Second, you make JavaScript debugging more pleasant, as you can easily step through the source code. Let’s see what’s required to have script files stored in binaries served from the file system instead. We’ll see it for the principal ASP.NET AJAX script file—MicrosoftAjax.js—which alone contains two-thirds of the Microsoft AJAX library . The technique is also valid for any custom script file, however.

Normally, you don’t take care of MicrosoftAjax.js—you just find it downloaded care of the script manager. If you examine the HTML source of an ASP.NET AJAX page, you can hardly find a reference to such a file. Here’s what you find instead:

<script src="/IntroAjax/ScriptResource.axd?d=...&amp;t=..."
        type="text/javascript">
</script>

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:

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

All of a sudden, the MicrosoftAjax.js script file is now referenced as shown here:

<script
    src="~/JS/System.Web.Extensions/1.0.61025.0/MicrosoftAjax.js"
    type="text/javascript">
</script>

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:

<DRIVE>:

Program FilesMicrosoft ASP.NETASP.NET 2.0 AJAX Extensionsv1.0.61025MicrosoftAjaxLibrarySystem.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

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:ScriptManager runat="server" ID="ScriptManager1">
  <Scripts>
    <asp:ScriptReference Name="MicrosoftAjax.js"
        Path="~/Scripts/MicrosoftAjax2.js" />
  </Scripts>
</asp:ScriptManager>

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" ID="ScriptManager1"
     OnResolveScriptReference="ResolveScript">
   ...
</asp:ScriptManager>

<script runat="server">
void ResolveScript(object sender, ScriptReferenceEventArgs e)
{
   // Changes the location of the script. Note scripts must
   // exist in this new location else load error.
   e.Script.Path = "~/MyScripts2/" + e.Script.Name;
   ...
}
</script>

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 <globalization> 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 a client 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 locale-specific 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.

<script type="text/javascript">
<!--
var __cultureInfo = '{"name":"it-IT", ... }';
// -->
</script>

The following code snippet shows how to consume culture information from the client:

<script type="text/javascript" language="Javascript">
function ShowDate()
{
    var d = new Date();
    var strDate = d.localeFormat("dddd, dd MMMM yyyy HH:mm:ss");
    $get('Label1').innerHTML = strDate;
}
</script>

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" ... %>
...
<asp:ScriptManager ID="ScriptManager1" runat="server"
     EnableScriptGlobalization="true" />

When you re-run the page, with the EnableScriptGlobalization flag turned on, you get a different result, as shown in Figure 3-2.

Showing culture-specific information in a globalized script

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:ScriptManager ID="ScriptManager1" runat="server"
     EnableScriptLocalization="true">
   <Scripts>
      <asp:ScriptReference Path="Person.js" ResourceUICultures="it-IT" />
   </Scripts>
</asp:ScriptManager>

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 Script Registration Model

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 <script> 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.

Script Error Handling

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.

Handling Debug and Release Scripts

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

Note

The ScriptMode property is totally ignored both on ScriptManager and ScriptReference when the retail attribute of the <deployment> element is set to true. In this case, the release versions of client scripts will be used throughout the Web site. Note that the retail attribute is set to false by default in machine.config.

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.

Table 3-6. Script Modes

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 <Scripts> section that provides a different value for ScriptMode. In the end, the Auto value is equivalent to Debug when the debug attribute of the @Page directive is true. In all other cases, it is equivalent to Release.

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 ID="ScriptManager1" runat="server" ScriptMode="Auto">
    <Scripts>
       <asp:ScriptReference path="person.js" ScriptMode="Inherit" />
    </Scripts>
</asp:ScriptManager>

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

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.

Custom Error Handling and Redirection

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.

An error occurred during a partial page refresh.

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 <customErrors> section in the web.config file and indicate HTML error pages to reach in case of specific errors. Here’s a quick example:

<customErrors mode="On" defaultRedirect="GenericErrorPage.htm">
    <error statusCode="404" redirect="site404.htm" />
    ...
</customErrors>

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”.)

<script type="text/JavaScript" language="JavaScript">
function pageLoad()
{
   // Register handlers for UpdatePanel client events
   var manager = Sys.WebForms.PageRequestManager.getInstance();
   manager.add_endRequest(endRequest);
}
function endRequest(sender, args)
{
    $get("ErrMessage").innerHTML = args.get_error().message;
    args.set_errorHandled(true);
}
</script>

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.

Incorporating the error message in the page

Figure 3-4. Incorporating the error message in the page

Conclusion

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.184.155