Script it with ADP.NET 2.0

This article was originally published in VSJ, which is now part of Developer Fusion.
As long as HTTP remains the underlying transportation protocol, a web application can’t be anything radically different from a number of pages bound to a public URL.

The inexorable progress of web-related technologies has not changed this basic fact, for the simple reason that it is the natural outcome of the simplicity of the HTTP protocol. ASP.NET is the summation of web development technologies that rapidly followed one another in the past ten years – one building over another, and each filling the gaps of its predecessor. As a result, ASP.NET is currently the most technologically advanced, feature-rich, and powerful platform for building distributed applications transported by the HTTP protocol.

Just like other web development technologies such as ASP, Java Server Pages (JSP), the open source web platform commonly referred to as LAMP (Linux plus Apache plus MySQL plus either Perl, Python, or PHP as the programming language), ASP.NET builds a layer of abstraction over the capabilities and the features of the HTTP protocol. If a given feature can’t be implemented using a mix of HTML, CSS, images, URL-based resources, and JavaScript, then there’s no way a server-side technology – whatever it is – can provide a replacement.

These considerations lead straight to the conclusion that the more ASP.NET makes progress as a platform, the more effectively using client-side script code marks the difference between ordinary and super web applications.

In this article, we’ll tour around the new script-related features you’ll find in ASP.NET 2.0, and two in particular: cross-page posting and script callbacks.

The need for scripting

Many of the common tasks a user repeatedly accomplishes each and every time he or she opens a page can be automated to some extent. For example, a piece of code in the ASP.NET page could automatically place the input focus on the first input field that appears in the page. This feature requires a few lines of Javascript code associated with the startup event of the page. Needless to say, the required changes can be added to virtually any kind of server page, regardless of the technology in use. Even pure HTML pages can easily incorporate this feature. So what’s the point?

The point is that a framework should hide the details and provide this (and other similar) capability out of the box. ASP.NET 2.0 makes some significant steps in the right direction. The key issue on the way to embedding any complex script code in a web page is just how to do that. You can have server controls write Javascript functions in the page’s markup by registering script code with the page. In ASP.NET 1.x, the Page class counts a variety of RegisterXXX methods to insert script code in various places and with different characteristics. In ASP.NET 2.0, even more methods are supported. However, the whole set of methods is now exposed out of the ClientScript object. Old-style methods are still accepted, but marked as obsolete. In other words, to register a startup script in an ASP.NET 2.0 application you should proceed as follows:

Page.ClientScript.RegisterStartupScript(…);
In ASP.NET 1.x you could put the method directly in the Page class. The drawback of registering script code with the page is that you have to provide the script as a string, which can sometimes be problematic. You can add the logic for building such strings on the page; alternatively, you can read the script code from an external source – for example, a resource string in one of the assemblies that make up the web site.

Many ASP.NET 2.0 controls take another approach – they simply link any needed script code to the page. Controls use the old-fashioned, but still effective, <script> tag with the src attribute set to a new and specific HTTP handler – WebResource.axd.

The URL that brings script code to ASP.NET pages that use rich controls takes the following form:

WebResource.axd?a=assembly&r=resourceName&t=timestamp
Parameters include the assembly that contains the script resource, the name of the resource, and a timestamp. The timestamp value is the current timestamp of the assembly and is added to make the browser download resources again should the assembly be modified.

WebResource.axd accesses the specified assembly, reads the contents of the given resource and serves it back to the requesting browser. This technique can be emulated in ASP.NET 1.x by simply writing and registering a HTTP handler that mimics the behaviour of WebResource.axd. As a matter of fact, many more ASP.NET controls add script code to the page, and in many cases it is really complex and long script code. In fact a great deal is added to ASP.NET pages with client script. But what about the browsers?

Why not scripting?

For a page to take advantage of script capabilities, two non-exclusive conditions must be met. First, the browser must accept JavaScript, and second, it must provide the script code access to a Document Object Model (DOM), with a rich set of methods and ideally with page update capabilities (Dynamic HTML). More than 90% of the browsers in use today are fifth-generation browsers like Internet Explorer 5.x and newer, Safari 1.2, Netscape 6.x and newer, Opera 7 and newer. All of these implement a nontrivial DOM and support most W3C standards. It makes sense to forget about Netscape 3.x and similar browsers and create script code that runs in accordance with recognized DOM standards.

Today, in ASP.NET an “advanced” control mostly indicates a control that supports client-side scripting. Common examples include GridView, TreeView and Menu. These controls benefit from the HttpBrowserCapabilities class to detect the capabilities of the underlying browser and generate code accordingly. Not only should you take advantage of these controls in your applications, you should also learn from them in case you’re called on to design and implement custom controls that use script.

In ASP.NET 2.0 two low-level script-related capabilities can be used to build more powerful and interactive applications. They are cross-page posting and script callbacks. Before looking at these features, let’s briefly review the methods of the Page class that have to do with scripting, as shown in the table below.

Methods on the class Page that relate to scripting

Method 2.0 Description
GetCallbackEventReference Yes Obtains a reference to a client side function that, when invoked, initiates a client call back to server-side events.
GetPostBackClientEvent   Calls into GetCallbackEventReference.
GetPostBackClientHyperlink   Appends javascript: to the beginning of the return string received from GetPostBackEventReference.
GetPostBackEventReference   Returns the prototype of the client-side script function that causes, when invoked, a postback.
IsClientScriptBlockRegistered Obsolete Determines whether the specified client script is registered with the page.
IsStartupScriptRegistered Obsolete Determines whether the specified client startup script is registered with the page.
RegisterArrayDeclaration   Use this method to add an ECMAScript array to the client page.
RegisterClientScriptBlock Obsolete An ASP.NET page uses this method to emit client-side script blocks in the client page just after the opening tag of the HTML <form> element.
RegisterHiddenField Obsolete Use this method to automatically register a hidden field on the page.
RegisterOnSubmitStatement Obsolete Use this method to emit client script code that handles the client OnSubmit event. The script should be a JavaScript function call to client code registered elsewhere.
RegisterStartupScript Obsolete An ASP.NET page uses this method to emit client-side script blocks in the client page just before closing the HTML <form> element.
SetFocus Yes -Sets the browser focus to the specified control.

Many of these methods are required to implement functions based on cross-page posting and script callbacks.

As mentioned earlier, not all methods marked as obsolete represent missing functionalities in ASP.NET 2.0. These methods have simply been refactored and grouped under a new client script manager object – the ClientScript property whose type is ClientScriptManager.

Cross-page posting

The typical ASP.NET page contains a unique <form> tag decorated with the runat attribute. On the server, such a <form> tag gets mapped to an instance of the HtmlForm class. The HtmlForm class is the container of all page controls and generates the actual markup for the page – everything in the page is inside a unique, all-encompassing HTML <form> element. This output is then displayed to the user. The obtained HTML form posts to the same URL as the displayed page. The action attribute of the <form> is automatically set to the originating URL and there’s no way to change it programmatically. In other words, you cannot make any ASP.NET page post its input data to another page. Each page can only post to itself.

In ASP.NET 2.0, posting data to different pages is possible, but the implementation of the feature depends on some new capabilities of button controls. Forms work in ASP.NET 2.0 the same way they do in ASP.NET 1.x. So what’s this new capability of the button control? It’s the feature known as cross-page posting.

The single form model doesn’t really impose a limitation on developers; not having system support for multiple forms in a page is not a great sacrifice. Some pages, however, would have a more natural design if they could define multiple forms. For example, imagine a page that provides some information to users but also needs to supply an additional form such as a search or a login box. This kind of page is quite common in portal applications. Since in ASP.NET multiple forms are just not permitted, you have to work out other approaches to the problem. For example, you can incorporate search or login capabilities in ad hoc classes, and call those classes from within the page the user is looking at. This may or may not be the right way to factorize your code¸ however, especially if you’re porting some old code to ASP.NET when you might find it easier to separate login or search code in a dedicated page. But in this case, how do you post input data to this distinct page?

ASP.NET prevents you from having multiple server <form> tags flagged with the runat attribute. However, nothing prevents you from having in the same ASP.NET page one server-side <form> tag and as many client HTML <form> elements as needed. Classic HTML forms devoid of the runat attribute are completely ignored by ASP.NET. In this case the markup served to the browser simply contains two <form> elements each pointing to a different action URL.

<form action=”page.aspx”>
:
</form>
<form action=”search.aspx”>
:
</form>
This code works just fine, but has a major drawback. When the action page is requested (say, search.aspx), you can’t use the ASP.NET programming model to retrieve posted data. When writing search.aspx, in fact, you can’t rely on viewstate to retrieve posted values.

Is this a real limitation? I’d say no, and for two good reasons. First, the browser programming model ensures that all input fields in the posting form are always packed in the HTTP payload. In other words, this information is always sent to the server. By using the old faithful Request object you can access any posted value at any time in the page lifecycle. The viewstate serves to track the story of the page controls, not to move fresh input data.

The second reason is that, even if viewstate support were enabled, to process search.aspx (or whatever action page you have) you don’t actually need all the input fields in all forms contained in the page. In many cases, you just need the input field of the posting form. The Request object might look obsolete, but trust me, it is still the fastest and most effective way of retrieving posted data.

Note that I’m not saying the whole ASP.NET model is overkill; if you need to have multiple forms, you can handle these additional forms the old way.

This long preamble makes it easy and straightforward to introduce and discuss the IButtonControl interface, introduced in the table below:

The IButtonControl interface

Name Description
CausesValidation Boolean value, indicates whether validation is performed when the control is clicked
CommandArgument Gets or sets an optional parameter passed to the button’s Command event along with the associated CommandName.
CommandName Gets or sets the command name associated with the button that is passed to the Command event.
PostBackUrl Indicates the URL that will handle the postback triggered through the button control. This ASP.NET 2.0 specific feature is known as cross-page postback.
Text Gets or sets the caption of the button.
ValidationGroup Gets or sets the name of the validation group which the button belongs to
Visible Boolean value, indicates if the button control is rendered.

Authoring a web page that can post data to another page requires only a couple of steps. First, you choose the controls that can cause postback and set their PostBackUrl property. It’s worth noticing that in this context a button control is any server control that implements IButtonControl.

<form runat=”server”>
	<asp:textbox runat=”server” id=”Data” />
	<asp:button runat=”server”
		id=”buttonPost” Text=”Click”
		PostTargetUrl=”target.aspx” />
</form>
When the user clicks the button, the current form posts its content to the specified target page. What about the view state? A new hidden field is created named __PREVIOUSPAGE that contains any view state information relevant to serve the request. To reference controls in the posting page, you use the PreviousPage property on the Page class. Here’s a sample target page that retrieves the content of a text box defined in the form:
protected void Page_Load(object sender, EventArgs e)
{
// Retrieves posted data
	TextBox txt = (TextBox)
		PreviousPage.FindControl(“TextBox1”);
	:
}
The PreviousPage property lets you access any input control defined on the posting page. Access to input controls is weakly typed and occurs indirectly through the FindControl method. The target page doesn’t know anything about the type of the posting page. PreviousPage is declared as a property of type Page and as such it can’t provide access to members specific to a derived page class.

If you know exactly the type of the calling page, then you can add the following directive to the target page:

<%@ PreviousPageType VirtualPath=”crosspostpage.aspx” %>
The directive can accept either of two attributes – VirtualPath or TypeName. The former points to the URL of the posting page; the latter indicates the type of the calling page. The preceding directive makes the PreviousPage property on the target page class be of the same type as the page at the given path (or the specified type) thus providing for a strong typed access to page members.

In addition to the new PostBackUrl property of button controls, since version 1.x, ASP.NET provides another mechanism for transferring control and values from one page to another – the Server.Transfer method.

In this case, the URL of the new page is not reflected by the browser’s address bar because the transfer takes place entirely on the server.

Script callbacks

Many developers would really like a tool capable of sending invisible queries to the server based on client side data, and updating the current page intelligently, changing only those portions of HTML markup affected by the results of the query. Especially when large pages filled with images and animations are employed, a page refresh poses a problem and impacts the overall performance. Smart postbacks that bring up to date information on the client without refreshing the whole page are much more than welcome. ASP.NET script callbacks are just this missing link needed to make this long-awaited feature possible.

A script callback consists of a standard piece of JavaScript code attached to a client-side button control. When clicked, the page won’t post back in the traditional way, but simply executes the bound code. Taking advantage of the browser’s infrastructure, the executing JavaScript code will open a separate socket to the server (same URL as the current page) and invokes a method. The return value of the method execution is carried back to the client and used to invoke a client-side callback function. What this client-side callback function does is application-specific. In most cases, it will use Dynamic HTML to update the current page intelligently. So much for the big picture, let’s discuss a practical example.

The button to trigger the operation has its client-side onclick attribute set as follows:

string callbackRef = ClientScript.
	GetCallbackEventReference(this,
		“document.forms[0].
		elements[
		‘cboEmployees’].value”,
		“UpdateEmployeeViewHandler”,
		“null”,
		“null”,
		false);

buttonTrigger.Attributes[“onclick”] =
		String.Format(“javascript:{0}”,
		callbackRef);
The this parameter indicates the object on the server to handle the callback request. In the example, this means the page; it could also be the instance of a page control. The second parameter is an expression that evaluated determines the input value (it can only be a unique string) for the invoked method. The third parameter is the name of the client-side JavaScript function that receives the return value of the method call. Other parameters indicate the context object to pass to the client callback, the error handler, and whether the operation should take place asynchronously or synchronously (default).

Who receives the client invocation? It’s the page object in the sample above. In general, it can be any object that implements the ICallbackEventHandler interface. A well-known interface is important because it determines the name and signature of the method to invoke on the server. The interface contains just one method:

string RaiseCallbackEvent(string)
The method receives the client input, does some work, and returns values for the client callback to update the page in the browser. The RaiseCallbackEvent method takes and returns a string. The contents and format of the string are strictly application specific.

The client callback has a mandatory prototype:

function UpdateEmployeeViewHandler(result, context)
{
	:
}
The result parameter matches the return value of the server-side method. The client callback can do whatever is useful to the page; however, it normally uses the browser’s DOM and Dynamic HTML support to update the page in a smart way. During all these operations the user doesn’t experience any page postback. Everything takes place in the background; the user will only see the final results of the operation – changes to small portions of the page.

ASP.NET script callbacks are a cross-browser feature, at least if we limit ourselves to considering fifth generation browsers. In Internet Explorer, the callback mechanism is implemented using an ActiveX control – the Microsoft.XmlHttp component. In Mozilla-based browsers (FireFox, Netscape 6.x, Safari), the same functionality is provided by an object exposed in the browser’s DOM. This object is called XMLHttpRequest. It is interesting to note that this object was originally added to Mozilla to mimic the behaviour of IE’s aforementioned ActiveX control.

Architecturally speaking, ASP.NET script callback works on a variety of browsers – let’s say on 90% of today’s browsers. However, while building real-world solutions around callbacks you should pay a lot of attention to the DOM calls you make to retrieve input data and to process and display results. A strict adherence to the DOM W3C standards is a must.

Summary

In this article, we’ve reviewed a few of the hot features you’ll find in ASP.NET 2.0 that relate to scripting. We’ve shown that programmers will find powerful tools in ASP.NET 2.0 to do good scripting in an easier and more effective way. Custom script can be imported in a more flexible way to accommodate the requirements of a particular application or control. Built-in script code blocks are integrated with the page when the page (or one of its controls) turns on a particular feature. Two new script-related features make the ASP.NET arsenal significantly more powerful than in earlier versions: cross-page posting and script callbacks. The former makes up for a well-known limitation of ASP.NET 1.x and allows you to click on a button and post some current data to another page in the same site or a different site. Script callbacks are a modern and effective way to do remote scripting. They let you invoke a server-side method from the client, get some results, and refresh only the parts of the page affected by the results.

With all these features, many of which are cross-browser too, there’s really no reason for not using them. Happy scripting!


Dino Esposito is an independent trainer and consultant, and author of Building Web Solutions with ASP.NET and ADO.NET, and the forthcoming Programming ASP.NET 2.0 Core Reference (both published by Microsoft Press). He is a regular speaker at Bearpark Publishing’s DevWeek conference – the 9th annual event is scheduled for 20–24 February 2006.

You might also like...

Comments

About the author

Dino Esposito United Kingdom

Dino Esposito is an instructor for Solid Quality Mentors, and a trainer and consultant based in Rome. He is the author of various books, including Windows Shell Programming, Instant DHTML Script...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil perpetrated by skilled but perverted professionals.” - Jon Ribbens