Exploring ASP.NET & Web Development

 

AJAX History

Browser History and the Back Button with ASP.NET AJAX

 

By Don Kiely

 

One of the problems with ASP.NET AJAX and, in fact, most all AJAX implementations is that the browser s back and forward buttons do not store the history of page state for AJAX requests. A browser goes only by URLs, and because the page address doesn t change when something causes an AJAX request, nothing is saved in the browser s history cache. Only when the user navigates to another page is history updated with the new page.

 

Microsoft provided a solution to the problem in service pack 1 for Visual Studio 2008 and the .NET Framework 3.5: AJAX History. I recently had the opportunity to explore this feature for ASP.NET 3.5 courseware I wrote for AppDev with Ken Getz; this article is based on that work.

 

AJAX History isn t enabled by default, and takes a little work to implement but it can greatly enhance users experience as they browse your site. The hardest part is deciding how to implement this feature in a way that makes sense for the user, given the set of controls on the page that are enabled with AJAX.

 

For example, say you have a page based on the Northwind database that displays a dropdown list with product categories. As the user selects different categories, the list of products in the selected category appears in a grid, which is wrapped up in an AJAX UpdatePanel. This makes the user interface smoother, with no page flashing but doesn t give the browser unique URLs to store in history. It is fairly easy to decide how to use history for the sample application. Each time the user selects a new product category, that page state should become a history point. A history point is saved state that you can navigate back to. Implementing history requires changes to the page (and some code).

 

You have to make a few changes to the ScriptManager control to implement AJAX History. You have to explicitly enable history by setting the EnableHistory attribute to true. You also have to supply a method for the onnavigate event that fires when the user navigates between pages. (I ll discuss the EnableSecureHistoryState attribute shortly.) The following code shows the ScriptManager definition in ProductsHistory.aspx:

 

<asp:ScriptManager ID="ScriptManager1" runat="server"

 EnableHistory="true"

 EnableSecureHistoryState="false"

 onnavigate="ScriptManager1_Navigate">

</asp:ScriptManager>

 

The other change needed in the page is to place the dropdownlist and its data source into the UpdatePanel. In the non-AJAX version of the page, these controls are located outside the UpdatePanel because they don t have to be updated as part of the AJAX request. In this new version of the page, with history enabled, the code has to change the state of the dropdownlist to return the page to a previous state, so the controls must be within the UpdatePanel.

 

The code behind the page needs two methods to implement AJAX History. The first method saves a history point in response to a new category selection by the user. The code starts by checking to see whether the page is in the midst of an asynchronous postback an AJAX request and makes sure that the user is not navigating to another page. If it is not an AJAX request and the user is navigating, you don t want to save the state.

 

If it meets these conditions, the code then creates a unique title for the page, concatenating the Northwind Products string with the name of the category. This is a good practice both so that the page title bar displays a meaningful title and because this will become the title displayed in the browser history, making it easy for the user to select a previous page state.

 

Then the code creates the history point using the ScriptManager s AddHistoryPoint method. This method has a few overloads, but the one used here passes a name for the query string parameter, the selected index in the categories dropdownlist, and the same page title created earlier in the code. This method saves the state by generating a unique query string that the browser can add to its history.

 

Following is the complete C# code for the list s SelectedIndexChanged event:

 

if (ScriptManager1.IsInAsyncPostBack

&& !ScriptManager1.IsNavigating)

{

string title = "Northwind Products: "

+ DropDownList1.SelectedItem.Text;

Page.Title = title;

ScriptManager1.AddHistoryPoint("category",

DropDownList1.SelectedIndex.ToString(),

title);

}

 

The state that you save with the AddHistoryPoint method is up to you. It needs to be enough to return the page to that previous state, no matter what the user has done in the meantime. The decision of how to define this state can take some hard thinking, depending on how a page operates.

 

The second method required to implement AJAX History is to respond to the ScriptManager s Navigate event. This event fires when the user is navigating to an item in the browser history list, passing in a HistoryEventArgs object that contains the state it reads from the query string parameter.

 

The code reads the state and checks to see if it is null or empty. If it is null or empty, it sets the selected index value to the first item in the list (zero). If state is available, it sets the selected index of the dropdownlist to the saved value, which causes the products for that category to reload. Following is the complete code for the ScriptManager s Navigate event:

 

string indexStr = e.State["category"];

if (string.IsNullOrEmpty(indexStr))

DropDownList1.SelectedIndex = 0;

else

{

int index = Convert.ToInt32(indexStr);

DropDownList1.SelectedIndex = index;

}

 

Now when the user selects different categories, ASP.NET generates a unique URL that the browser can save in history to return to a particular AJAX state. The URL looks something like this, with ASP.NET adding the query string parameter based on the code you wrote:

 

http://localhost:1414/Extensions/ProductsHistory.aspx#&&category=3

 

There is one potential problem with how the page is implemented: State information is visible to the user in the URL in the category query string value. This could be a security problem, so, by default, ASP.NET encrypts the query string value. You can control this with the EnableSecureHistoryState attribute of the ScriptManager control, which is set to true by default. Set to true, you ll find that the URLs now look something like this:

 

http://localhost:1414/Extensions/ProductsHistory.aspx#&&DwIbkbGQcKwH0/g6...

 

Used where appropriate, AJAX History can make pages much more usable for users, making it easier to return to different versions during a single session. Users also can bookmark various page states, making it easier to return to them later.

 

Don Kiely, MVP, MCSD, is a senior technology consultant, building custom applications as well as providing business and technology consulting services. His development work involves tools such as SQL Server, Visual Basic, C#, ASP.NET, and Microsoft Office. He writes regularly for several trade journals, and trains developers in database and .NET technologies. You can reach Don at mailto:donkiely@computer.org and read his blog at http://www.sqljunkies.com/weblog/donkiely/.