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.
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.
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
Events of the ScriptManager Control
Table 3 details the two events fired by the ScriptManager control.
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.
Figure 1. An error occurred during a partial rendering operation
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
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
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.
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.