Incorporating AJAX into a website is something that web developers have latched onto. Certainly it can provide a better user experience, but that can come at the loss of features the end-user is familiar with. This past week, I encountered this very scenario. The details are pretty basic: On my page I have a search bar at the top that allows the user to specify a couple of search parameters. These parameters get fed into a select sql query, and the results are shown in a GridView. The GridView has paging and sorting enabled, and in the previous classic asp version, the users have become accustomed to using the browser navigation buttons to move between pages and searches viewed. The GridView is within an UpdatePanel that is triggered by the Button in the search panel.
If you have ever done any work with UpdatePanels, you will realize where I am going with this (or you could just read the title of the post…). Once the GridView is in the UpdatePanel, the browser doesn’t see it as a traditional postback and no entry is added to the browser’s history stack. I can imagine how many phone calls I would receive: “I was on page 5 of the results, and when I hit the back button, it took me back to the home page…” Not something I want to have to deal with. Surely there must be a way to use UpdatePanels and still use the browser’s history stack.
ASP.NET 3.5 Extensions Preview
There are a few different pieces to the 3.5 Extensions Preview. One of these pieces provides a new System.Web.Extensions.dll, version 3.6.0.0 that gives the ScriptManager the ability to manage history for us. The installer for the Extensions Preview will place the new versions of the included dlls into the GAC. Once this is done, everything is ready to take advantage of this new feature in just a few short steps.
- Create a new ASP.NET 3.5 Extensions Web Site. If you want to integrate the navigation functionality into an existing website, you will need to modify the web.config file to reference the 3.6.0.0 dlls. Create a new Extensions Web Site and use that to update your existing website.
- Set the EnableHistory property on the ScriptManager to True.
- Add history entries to the ScriptManager when the page state should be saved.
- Handle the Navigate event on the ScriptManager to load the page to the previous state.
That’s it – simple!
Configuring the ScriptManager
There are two properties of interest here. ScriptManager.EnableHistory should clearly be set to True. The other property of interest is ScriptManager.EnableStateHash. When this property is set to False, the state will be stored as plaintext at the end of the url (after any querystring parameters). Setting EnableStateHash will hash the state to protect the data and prevent tampering.
Adding History Entries
In the scenario mentioned above, I want to save the state of the page whenever a column is sorted or the user selects a new page index. In the Paged and Sorted event handlers for the GridView I will call my AddHistoryPoint helper function. The code is pretty simple, so I will let the code speak for itself. Last post was in C#, so this one is in VB.NET.
Protected Sub AddHistoryEntry()
Dim sm As ScriptManager = ScriptManager.GetCurrent(Me.Page)
If sm.IsInAsyncPostBack AndAlso Not sm.IsNavigating Then
Dim state As New NameValueCollection()
state("field1") = field1.Text.Trim()
state.Add("sort", gvResults.SortString)
state.Add("page", gvResults.CurrentPage)
ScriptManager.GetCurrent(Me.Page).AddHistoryPoint(state, "Custom Title")
End If
End Sub
A couple of things should be called to attention here. First, notice that the state is only stored if two conditions are met. The first is to avoid duplicates. If the page is not in an async postback, that means it is in a regular postback and that the browser will already have an entry in the history stack. The second is to ensure that an infinite loop doesn’t start. When a saved state is requested, it must navigate to that state. If that involves changing the page index, that means that the Paging event will be fired. As this is when the new state is stored, an infinite loop would eventually result in a stack overflow. Once those two conditions are met, the new state can be saved. If you are just saving a single field, you do not need to
Navigating to a Saved State
When the back or forward button is used in the browser, the ScriptManager will pick up that action and fire the Navigate event.
Protected Sub ScriptManager1_Navigate(ByVal sender As Object, ByVal e As System.Web.UI.HistoryEventArgs)
field1.Text = e.State("field1")
gvResults.SortString = e.State("sort")
gvResults.CurrentPage = e.State("page")
' Check if this is a blank entry form
If Not e.State.HasKeys() Then
pnlResults.Visible = False
upResults.Update()
Else
BindData(sender, e)
End If
upSearch.Update()
End Sub
So once again, this is pretty simple code. The first 3 lines restore the state of the parameters of my page. I need to update the textbox to reflect the data used in the GridView. If the state is empty, i.e., if the initial page load is requested again, I hide the results as they are originally. If there is a state to bind, then I bind the data. My UpdatePanel is set to UpdateMode=”Conditional”, so updating that is the last piece of the puzzle.
That wraps up the post, thanks for reading! 😛