Published: 23 Jan 2008
By: Bertrand Le Roy

One common drawback of Ajax applications is the loss of the browser's back button. This article by Bertrand Le Roy shows how to restore it using ASP.NET 3.5 Extensions Preview and server code.

A brief history of History

ASP.NET has introduced a very powerful component model for the Web at a time when templating engines were limited to inserting code into markup placeholders. The new level of abstraction aims at reproducing the component and event-based model that was familiar to VB developers on the Web, along with the productivity boost it represented. At the time, Ajax was a concept that was almost limited to a few big applications such as Outlook Web Access and that didn't even have a clearly defined name. Most of the interactivity was implemented on the server-side, which means that a round-trip to the server was necessary for each and every user interaction. This, in addition to the disconnected nature of HTTP and the scalability requirements of Web applications, set state management as one of the most important problems that a Web framework had to solve. It also meant that a lot of redundant data was travelling back and forth as the server had to reconstruct the application's state on the one side, and the browser had to reconstruct the page UI on the other, every single time the user was doing anything. One advantage of this system is that every user interaction resulted in the browser creating a point in History and adding it to its back button's stack of previous states, making it possible and relatively natural for the user to step back. If anything, there was an excess of history points.

It was only a matter of time before the industry moved to a more rational model where the client-server conversation becomes a lot less chatty and redundant. This is essentially what Ajax is all about, but switching to a model where the page gets its updates out of band instead of posting back and rebuilding itself from scratch means that the browser has no clue that anything significant happened and no history points get created. Essentially, we went from too many history points to none at all!

In ASP.NET Ajax, you can go all the way and implement a lot of logic on the client-side, which involves some investment in the understanding of client-side technologies, or you can continue to implement the logic in server-side code using UpdatePanels, or even have a mix of UpdatePanels and client-side code. In all three cases, ASP.NET provides a simple and efficient way of restoring the back button. What's interesting is that it is now up to the application developer to decide which user interactions represent a change that should be reflected by a point in browser history. In summary, we're now moving from no history points to the exact right number of history points.

An important side-effect of implementing browser history in an Ajax application is that each state of the application becomes bookmarkable.

How to prepare an application for history

To make a page work well with the back button, you'll need to determine the minimum set of variables that is necessary to represent its state. For a mapping application, this may be the current longitude and latitude and for a wizard it may be the current step index and maybe the contents of the different fields in each step. But one thing to keep in mind is that this set of variables needs to remain small because of the way history management systems have to be implemented. In effect the implementation is severely constrained by the fact that browsers don't have built-in APIs to manage the history stack as we speak (the next generation of browsers will very likely change that, seeing how important this scenario has become). The only place where the framework can store state is in the so-called fragment part of the url (the part after ‘#', which was originally designed to enable links within a single page). This is a very different process than the ViewState model where everything is automatically maintained (and as a consequence can become very large if one is not careful).

It is actually a very useful and structured design phase to stop and think about what constitutes the state of the application.

A simple Ajax wizard

Let's start with a simple wizard inside of an UpdatePanel:

The minimum state that we want to maintain here is the wizard's step index, and that's what we'll do here. Depending on what use we want to make of this page, we may also want to store the contents of each textbox, but these contents could potentially be quite large, so if it's not strictly necessary, I'd keep them out. An alternative for such potentially large pieces of state is to use a more traditional state store such as ViewState or Session, but of course you won't get history or bookmarkability on them.

Handling state changes and navigation

To handle history, once we've set EnableHistory to true, we only have two things left to do. The first one is to create history points every time state changes. In our case, this is done by handling the ActiveStepChanged event of the wizard and creating a history point from there:

Listing 1: C# code

Listing 2: VB code

It is important to note that although we only have one piece of state in this sample (the wizard index), you can handle as many events as you want and independently create history points from there. Each part of the application can manage its own part of the state without having to know about the others. In other words, state is added by AddHistoryPoint more than it is set, and setting a new entry doesn't delete the previously added ones.

The second thing we need to do is handle the Navigate event on the ScriptManager, which gets triggered every time the user clicked the back or forward button and restore the state from there:

Listing 3: C# code

Listing 4: VB code

You can think of these two things as analogous to SaveViewState and LoadViewState in the ViewState model.

An important thing to note here is that when restoring the state, our code has to account for empty state and restore the default state of the page in that case. This is to handle the case of the user going back from a later state of the page to the initial request to the page, where no state existed yet.

One could notice while running this application that the state on the URL looks very cryptic. Indeed, by default the server state is hashed using the same algorithm as ViewState. This can be relaxed by setting EnableStateHash to false on the ScriptManager. In that case, the state becomes readable (and modifiable) and looks very much like a regular query string:

Listing 5: EnableStateHash = true

Listing 6: EnableStateHash = false

It's also important to say a word about security here. Even if the state is hashed, the data that the application puts in there is essentially user data, and there is potential for injection attacks. It is thus very important to validate that data before using it, as with any piece of user data.

Summary

By writing two simple server-side event handlers, we've enabled meaningful state and history management on a sample application. Furthermore, we've done that without writing a single line of client-side JavaScript. This is made possible by all the work that has been put into the history feature of ASP.NET Ajax, which abstracts away browser differences to a large extent (there are still some caveats on Safari 2 and Internet Explorer, and Opera versions before 9.5 are broken). This will enable productive development of user-friendly applications that behave in a natural way and don't break Web users' expectations.

References

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Bertrand Le Roy

Bertrand Le Roy is a program manager in the ASP.NET team.

This author has published 3 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Animating a Web Form using ASP.NET AJAX
In this article you will learn how to animate a webform using asp.net ajax.
Jquery Ajax
JQuery makes communicating with the server very, very easy. JQuery uses get(), getJSON(), and post(...
jQuery Deferred Objects Promise Callbacks
jQuery Deferred/Promise objects untangle spaghetti-like asynchronous code.
jQuery in Action 2nd edition: Queuing functions for execution
This article is taken from the book jQuery in Action, second edition. This segment shows how you can...
Test120Jan
This is my custom article

You might also be interested in the following related blog posts


Adding users to a TFS project when youre not on the domain read more
Navigation for CoverFlow read more
Web Deployment Tool has gone RTW read more
An alternative to Crystal read more
Take Two: A jQuery WCF/ASMX ServiceProxy Client read more
Building Interactive User Interfaces with Microsoft ASP.NET AJAX: Triggering Full Page Postbacks From An UpdatePanel read more
Understanding Reverse Proxy Servers and the Mailman read more
Session State Not Working? Check Your Web Garden! read more
Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 6: Data Transfer Objects (DTOs) read more
AJAX Logging with Exponential Backoff read more
Top
 
 
 

Discussion


Subject Author Date
placeholder NIce article !! Mehfuz Hossain 1/23/2008 11:25 AM
RE: NIce article !! Kazi Manzur Rashid 1/24/2008 4:54
placeholder My fave feature of ASP.NET AJAX to date Granville Barnett 1/23/2008 12:15
Thanks for this Kent Sharkey 1/23/2008 3:01
placeholder history error Alex Grach 1/29/2008 1:12 AM
RE: history error Bertrand Le Roy 1/29/2008 4:41
placeholder Sweet Patrick Smith 1/31/2008 12:10 AM
Nice Copy. Why not try with some new Example? Moshiur Murshed 1/31/2008 9:21 AM
placeholder RE: Nice Copy. Why not try with some new Example? Bertrand Le Roy 1/31/2008 3:06
RE: Nice Copy. Why not try with some new Example? Bertrand Le Roy 1/31/2008 3:31
placeholder Gee, thanks! Bertrand Le Roy 1/31/2008 3:05

Please login to rate or to leave a comment.