Just a few weeks after the release of the .NET Framework 3.5 and Visual Studio 2008 in mid November, Microsoft shipped the first CTP of a new toolkit for ASP.NET developers - the ASP.NET Extensions 3.5. The toolkit contains a selection of features that were already distributed as part of the latest ASP.NET Futures build, back in July 2007. The ASP.NET Extensions 3.5 will probably make its way into the Service Pack 1 of the .NET Framework 3.5 in late 2008. It is centered on a new version of the system.web.extensions assembly numbered as version 184.108.40.206.
ASP.NET Extensions 3.5 includes a new set of helper controls for Silverlight programming, as well as a new family of data-bound control (Data Dynamic controls). In addition, it brings to the masses a new build of ADO.NET Data Services (formerly, Astoria services) and the first binaries of the MVC framework.
In this article, though, I'll focus on yet another feature of ASP.NET Extensions 3.5 - the native support for history points and back/forward browser navigation.
The Different Nature of AJAX Pages
The essence of the AJAX revolution is all in the fact that now pages can be designed and authored to perform most of their tasks without making full postbacks to the server. The user interface of a given page remains up and running all the time, until it is explicitly dismissed by the user when he or she navigates to another page. What if the user wants to undo the latest action? Unless the Web application provides a made-to-measure model for rolling back certain operations, all that the user can do is move back to the previous page. On the other hand, the Back button of browsers exists just to let users go back to the previous state of the application. Likewise, the Forward button was designed to let users move onward from a previous page that they reached via the Back button. Normally, browsers supply a client-side programming model - the history object in the Browser Object Model (BOM) - for executing back and forward movements programmatically.
Regardless of the technology used to create it, an AJAX application tends to replace URL-to-URL browser navigation with script-driven HTTP requests. The history of an AJAX application doesn't necessarily coincide with the list of visited URLs. More likely, an AJAX history is a list of action points scattered through one or a few pages. In a nutshell, AJAX breaks the assumption that the previous state of a Web application coincides with the previously visited URL. This is a big change and can be seen as the offspring of the new paradigm that AJAX pushes for Web applications. The net effect is that all the user interaction with an AJAX page produces a single entry in the browser's history. Hence, when you click the Back button you are redirected to the previously visited distinct URL which may be an entirely new page even in a different application.
As of today, browsers do not provide in their BOM an API to add significant states of a page to the global browser history. Worse yet, browsers lack the notion of a save-point - that is, a significant state in the page lifetime that you want to store for returning later. Browsers only add URLs to their history, and only URLs that you have reached through the browser itself. With AJAX pages, instead, you often reach URLs using a distinct HTTP engine based on the XMLHttpRequest object. All of this navigation would be lost, unless the Web framework (i.e., ASP.NET Extensions 3.5) does some smart tricks.
The History Extensions to ASP.NET AJAX
It should be noted that the Web framework can't just replace the browser's history with its own engine. The issue is that you need the framework to do something that provides a new feature (page save-points) in the context of tools that only recognize URLs and, more, lack an API to extend the history list programmatically.
In ASP.NET Futures July 2007, Microsoft introduced a new server control for custom history management - the History control.
In ASP.NET Extensions 3.5, the History control has been removed. However, the functionality is now incorporated in the ScriptManager control. Here's the code you need in an ASP.NET AJAX page to track specific page states and save those states for later. Note that what I previously called a page save-point is referred to as a history-point in the ASP.NET Extensions 3.5 naming convention.
History support is disabled by default meaning that you need to set the property EnableHistory to true in order to enable it. The ScriptManager fires the Navigate server event whenever the user navigates to a URL that represents a previously saved page state. By handling the Navigate event, the developer takes control of the special page request and manages to bring the page back to the requested state. The history point, in fact, is an abstraction and doesn't match a physical page that the browser can autonomously render. The history point is bound to a fake URL created by the ScriptManager control. This fake URL is the original URL plus some user-defined state information. Let's delve deeper.
Adding History Points
Consider a page with an UpdatePanel control wrapping a DetailsView and a SqlDataSource. The user may turn the DetailsView in edit or read-only mode and through the set of bound records. Here's an excerpt from the code-behind class of a sample page:
AddHistoryPoint method on the script manager class creates an entry in the browser's history stack and makes it point to the page URL mangled with some extra data. In this case, the
AddHistoryPoint method is invoked in
ModeChanged events. In other words, when the DetailsView changes its mode or displays a new record, a bookmark to the current state is added so that the user could navigate back there using the Back button.
AddHistoryPoint method takes two arguments: the name of the parameter representing the state and the state value. Because this information is appended to the URL, you should manage to keep it quite cryptic and short. In the previous example, I'm using one letter to indicate "page" or "mode" followed by the concrete state information: the page index or the mode. This is arbitrary and should be adapted to the specific context.
The user then sees this page URLs added to the history list. Whenever one of these pages is reached, the
Navigate event is fired. By handling this event in managed code, you restore the proper state acting on the properties of all involved controls. The following code moves the DetailsView to the specified page and restores the specified state as appropriate.
Note that in this example the
ToInt32 is not a native method of the System.String class. It is rather a C# 3.0 extension method added to the sample project. It wraps the classic API to convert strings to numbers.
Lack of full control of the browser history is one of the key drawbacks of ASP.NET AJAX and AJAX in general. Although limited by the browser's support for Back and Forward buttons, the History API is powerful and generic enough to let you abstract history points and create bookmarks for them in code. We examined the server-side API. However, an equivalent API does exist also for the client side, but I will dig it out in the next article.
Dino Esposito is one of the world's authorities on Web technology and software architecture. Dino published an array of books, most of which are considered state-of-the-art in their respective areas. His most recent books are “Microsoft ® .NET: Architecting Applications for the Enterprise” and “...
This author has published 54 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.