The main control in the server infrastructure of ASP.NET AJAX is the ScriptManager control and its twin, the 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 to reference the original script manager from content pages.
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 that we covered earlier in this chapter. The ScriptManager
control also enables features such as partial-page rendering and Web
service and page method calls. The following code shows the simplest
and most common way to insert the script manager in an ASP.NET page:
<asp:ScriptManager runat="server" ID="ScriptManager1" />
The control produces no user interface, works exclusively on the server, and doesn’t add any 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 1 details the supported properties.
Table 1. 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 this
property is 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. |
RoleService | Gets an object through which you can set preferences for the client-side role service. This property is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. |
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. |
The
script manager is the nerve center of any ASP.NET AJAX pages and does
all the work that is necessary to make AJAX features function as
expected. Enabling AJAX features mostly means injecting the right piece
of script in the right place. The script manager saves ASP.NET
developers from dirtying their hands with JavaScript.
Methods of the ScriptManager Control
Table 2 lists the methods defined on the ScriptManager control.
Table 2. Methods of ScriptManager
Method | Description |
---|
GetCurrent | Static method, returns the instance of the ScriptManager control active on the current page. |
GetRegisteredArrayDeclaration | Static method, returns a read-only collection of ECMAScriptThis method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. array declarations that were previously registered with the page. |
GetRegisteredClientScriptBlocks | Static method, returns a read-only collection of client script blocks that were previously registered with the ScriptManagerThis method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. control. |
GetRegisteredDisposeScripts | Static method, returns a read-only collection of dispose scripts that were previously registered with the page. This method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. |
GetRegisteredExpandoAttributes | Static method, returns a read-only collection of custom (expando) attributes that were previously registered with the page. This method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. |
GetRegisteredHiddenFields | Static method, returns a read-only collection of hidden fields that were previously registered with the page. This method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. |
GetRegisteredOnSubmitStatements | Static method, returns a read-only collection of onsubmitThis method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. statements that were previously registered with the page. |
GetRegisteredStartupScripts | Static method, returns a read-only collection of startup scripts that were previously registered with the page. This method is not available in ASP.NET AJAX Extensions for ASP.NET 2.0. |
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.
Note
Script
registration is an old feature of ASP.NET, in spite of the slight
changes that occurred in the transition from version 1.x to 2.0. To
most developers, script registration is a pretty neat and clear
feature. However, the advent of ASP.NET AJAX extensions mixed things up
a little bit. What’s the difference between RegisterXXX methods in the ScriptManager control and the page’s ClientScript object, which is an instance of the ClientScriptManager class? ClientScriptManager’s and ScriptManager’s registration methods serve the same purpose but in radically different scenarios. You need to use the ScriptManager’s
methods only if you need to emit script code during an AJAX partial
rendering postback operation. An AJAX partial rendering postback
operation is processed by the runtime as usual, except for the
rendering stage. At this time, the markup is generated and any
registered script is emitted. Because during AJAX postbacks the ScriptManager is responsible for the markup rendering, it’s the ScriptManager that needs to know about registered scripts to emit. If you stick to using ClientScriptMananager’s
methods in an AJAX page, you risk that no script will be emitted during
the refresh of an updatable panel. As a result, a portion of your page
might display strange and weird behaviors. |
Events of the ScriptManager Control
Table 3 details the two events fired by the ScriptManager control.
Table 3. 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 ResolveScriptReference event, you can change the location from where the script is going to be downloaded on the client:
protected void ResolveScript(object sender, ScriptReferenceEventArgs e)
{
// Check Path or Name on the e.Script object based on what you've put in Scripts.
// Next, you specify the real file to load
if (String.Equals(e.Script.Path, "personal.js", StringComparison.OrdinalIgnoreCase))
e.Script.Path = "person.js";
}
By handling the AsyncPostBackError event, you can edit the error message being returned to the client. Here’s an example:
protected void AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
ScriptManager sm = sender as ScriptManager;
if (Request.UserHostAddress == "127.0.0.1")
{
sm.AsyncPostBackErrorMessage = String.Format(
"<b>An error occurred. <br/>{0}<b>",
e.Exception.Message);
}
else
{
sm.AsyncPostBackErrorMessage = String.Format(
"<b>An error occurred. <br/>{0}<b>",
"Please contact your Web master.");
}
}
When executed locally, the client-side error message appears as you see in Figure 1.
What
if you don’t like to display the message directly in the page 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.
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.
Note
When
an exception is thrown during a partial rendering operation, the HTTP
request returns a regular HTTP 200 status code, but instead of
including the updated markup, it includes a full description of the
error. In ASP.NET AJAX Extensions for ASP.NET 2.0, the default error
handler pops up a client-side message box with the exception message or
any text you assign to the AsyncPostBackErrorMessage property. In ASP.NET 3.5, on the other hand, you get a JavaScript exception. |
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
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, RoleService, and ProfileService. If you need to modify anything else, refer to the GetCurrent static method of the ScriptManager class. |
Script Binding and 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. The only HTML-supported way of linking
script files is the <script> tag and its src attribute. The ScriptManager control can be used to save yourself the direct manipulation of quite a few <script> tags and also obtain richer features, such as built-in management of localized scripts.
You use the Scripts collection to tell the ScriptManager
about the scripts you want to add to the page. The collection can be
accessed either declaratively or programmatically. In addition to the
user-requested scripts, the ScriptManager
control automatically emits in the client page any ASP.NET AJAX
required script. This means that, as a page developer, you don’t have
to worry about linking the Microsoft AJAX library or any other ASP.NET
AJAX native feature. The following example illustrates the script
loading model you can use to load optional and custom scripts:
<asp:ScriptManager runat="server" ID="ScriptManager1">
<Scripts>
<asp:ScriptReference
Name="YourCompany.ScriptLibrary.CoolUI.js"
Assembly="YourCompany.ScriptLib" />
<asp:ScriptReference
Path="~/Scripts/MyLib.js" />
</Scripts>
</asp:ScriptManager>
Table 4 lists the properties of the ScriptReference class by means of which you can control the loading of scripts.
Table 4. Events of ScriptManager
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 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. |
You
can reference script files, including ASP.NET AJAX system scripts,
either from an assembly or from a disk file. There’s a benefit in using
disk files. You gain something in performance because less work is
required to load the script in memory directly from a file. We’ll see
how to reference from disk 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="/Core35/ScriptResource.axd?d=...&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 an old acquaintance, the 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.
To
load a system file from disk, or to load a manually modified version of
a system script file, you create a directory structure that roots under
a custom folder the following subdirectories:
System.Web.Extensions\3.5.0.0
Now set the ScriptPath property on ScriptManager to a custom parent folder specific to your application. Say, you call it 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/3.5.0.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.
Handling Debug and Release Script Files
One of the additional free services offered by ScriptManager that isn’t offered by the classic <script>
tag is the ability to automatically link debug or release script files,
as appropriate. ASP.NET 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.
In
general, the main difference between debug and release scripts is that
the release scripts remove unnecessary blank characters, comments,
trace statements, and assertions. Normally the burden of switching the
links to debug and release scripts is left to the developer.
The ScriptManager
control takes on this burden and, based on the aforementioned naming
convention, distinguishes between debug and release scripts. The ScriptManager control picks debug scripts when the debug attribute of the <compilation> section in the web.config file is true.
Localized Scripts
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.
Localization is driven by the UICulture attribute in the @Page directive and the UICulture property in the Page class:
<%@ Page Language="C#" UICulture="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 referenced script. 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 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 UI culture is set to it-IT, the ScriptManager object attempts to retrieve a script file named person.it-IT.js from the same path.
Script Globalization
Globalization
is a programming feature that refers to the code’s ability to support
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?
When the EnableScriptGlobalization property is true, the ScriptManager emits proper script code that sets up a client-side global Sys.CultureInfo
object that JavaScript classes can consume to display their contents in
a culture-based way. Only a few methods and a few JavaScript objects
support globalization. In particular, it will work for the localeFormat method of Date, String, and Number types. Custom JavaScript classes, though, can be made global by simply calling into these methods or accepting a Sys.CultureInfo object in their signatures.