When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, October 8, 2008

Building Interactive User Interfaces with Microsoft ASP.NET AJAX: Enabling Bookmarking and the Browser's Back Button

By Scott Mitchell


A Multipart Series on ASP.NET AJAX
Over the past several years web developers have started using JavaScript to make asynchronous postbacks to the web server that only transmit and receive the necessary data; these techniques are commonly referred to as AJAX. Microsoft has released a free AJAX framework for ASP.NET developers named Microsoft ASP.NET AJAX. This article series examines using Microsoft's ASP.NET AJAX framework to build responsive user interfaces.

  • AJAX Basics and Getting Started with Microsoft's ASP.NET AJAX Framework - examines AJAX basics and looks at installing Microsoft ASP.NET AJAX; includes a demo of the UpdatePanel control.
  • Using the UpdatePanel - provides more in-depth and real-world examples of using the UpdatePanel.
  • Providing Visual Feedback with the UpdateProgress Control - shows how to use the UpdateProgress control to display visual feedback to the user when a partial page postback is in progress.
  • Performing Client Actions in Response to Partial Page Postbacks - learn how to execute client-side script in response to events raised by the partial page postback life-cycle.
  • Using the Timer Control - see how to asynchronously update a portion of the screen every n milliseconds.
  • Enabling Bookmarking and the Browser's Back Button - learn how to add history points to an AJAX-enabled web page so that your visitors can use their browsers' Back and Forward buttons and bookmarks.
  • Retrieving Server-Side Data Using Web Services - call Web Services from an ASP.NET AJAX application with a sprinkle of JavaScript.
  • A Look at JSON Serialization - explore the serialization format used by the ASP.NET AJAX Framework when calling Web Services from client-side script.
  • Triggering Full Page Postbacks From An UpdatePanel - learn how to override an UpdatePanel's default behavior and trigger a full page postback.
  • Refreshing An UpdatePanel With JavaScript - see how to refresh the contents of an UpdatePanel via JavaScript.
  • Rebinding Client-Side Events After a Partial Page Postback - discover how to automatically rebind client-side event handlers to HTML elements in an UpdatePanel after a partial page postback.
  • (Subscribe to this Article Series! )

    Introduction


    AJAX applications offer a more interactive user experience by replacing traditional full page postbacks with leaner and more efficient partial page postbacks. These partial page postbacks are executed asynchronously using JavaScript code in the browser. When a web surfer clicks on a link or submits a form (via a full page postback) the browser automatically adds the page being left to the browser's history. This allows the web surfer to use his Back and Forward buttons to navigate through this history. However, the partial page postbacks performed by AJAX applications do not cause the browser to register anything in their history. As a consequence, if a user visits an AJAX-enabled web page, performs a number of partial page postbacks, and then clicks the Back button, she is not returned to the state of the page prior to the last partial page postback. Instead, she is taken back to the page she was at before arriving at the AJAX-enabled web page.

    The good news is that starting with ASP.NET 3.5 SP 1, the ScriptManager control in the ASP.NET AJAX Framework includes functionality for creating history points in an AJAX-enabled web page. Adding a history point creates an entry in the browser's history for a particular page state. What's more, this page state is encoded in the querystring of the browser, meaning that visitors can bookmark a particular state of an AJAX application.

    This article shows how to add history points using the ScriptManager control. In particular, it shows how to record history points whenever the user pages or sorts a GridView. Read on to learn more!

    - continued -

    A Quick Overview of the ASP.NET AJAX History Feature


    As noted in the Introduction, the history feature became part of the ASP.NET AJAX Framework in ASP.NET version 3.5, SP 1. (For a link to download SP1, and for more information on the features included in SP1, see: ASP.NET in .NET 3.5 Service Pack 1.) It was originally part of the Microsoft ASP.NET Futures, although at that point in time this functionality was implemented using a History Web control. In the ASP.NET 3.5 SP1 release, this functionality was moved to the ScriptManager control.

    You Will Need ASP.NET 3.5 SP1 to Run the Demo
    Each installment of this article series has included a demo available for download. This installment is no different. However, the previous installments worked with ASP.NET version 2.0 and up. Because this installment's demo uses features only found in ASP.NET 3.5 SP1 it only works with that version of ASP.NET and beyond. If you do not have ASP.NET 3.5 SP1 installed, you can download the free Visual Web Developer 2008 and use it for running this article's demo. The Visual Web Developer installation will install the appropriate framework version for you, if needed.

    The history feature allows a page developer to create "states" in the lifecycle of an AJAX-enabled web page. Such a "state" is created by adding a history point. Once defined, these "states" are accessible from the browser's Back button; moreover, they can be bookmarked. If you use GMail as your online email account then you've already encountered an AJAX application that uses such history "states." Whenever you read an email in GMail, the email message is loaded asynchronously via JavaScript calls back to Google's servers. Your browser's querystring is also updated to include the ID of the message. If you hit the Back button you return to your GMail Inbox (and not the page you were visiting before going to GMail). Likewise, you can bookmark the page you are on while reading a particular email message. If you later visit that bookmark the GMail interface will automatically load the same mail message.

    The ASP.NET AJAX history feature works in the following manner:

    • You, the page developer, decide what actions are cause for a history point to be added. These actions can be client-side actions (clicking on an HTML element) or server-side actions triggered by a partial page postback. The set of actions that should cause a history point depend on the web page and its user interface. The download available at the end of this article includes two pages that display a sortable and page-able GridView. In one page, each sort action causes the insertion of a history point; in the other, both sorting and paging actions cause the insertion of a history point.
    • When an action that should cause the insertion of a history point fires, you need to add a history point. This is done by creating some string that models the state and registering that history state with the ScriptManager control. You can also update the page's Title based on the state so that the browser's title bar and history information includes an apt description for each state.
    • Finally, you need to create an event handler for the ScriptManager's Navigate event. This event fires whenever the user reaches the page by clicking the Back or Forward buttons or when coming in from a bookmark. The Navigate event handler is responsible for restoring the history state.
    In short, the history feature allows you to say, in response to certain user actions, "I want to save the application's current state." Doing so injects the current page information in the browser's history and encodes the state that you specify in the querystring. Later, if a user returns to this "state" by hitting the Back or Forward buttons or via a bookmark, you must restore that state.

    The remainder of this article looks at how to save a history point whenever a GridView is sorted, and how to restore that state when needed. Let's get started!

    Paging and Sorting Through Products in an AJAX-Enabled Web Page


    Before we concern ourselves with ASP.NET AJAX's history feature, let's start by creating and demonstrating the standard AJAX behavior with regard to the Back button and bookmarks. The demo available for download at the end of this article includes an ASP.NET page named StandardBehavior.aspx that contains a sortable and page-able GridView that lists the contents of the Products table in the Northwind database. The GridView is placed within an UpdatePanel and therefore any postbacks triggered by the GridView are handled as partial page postbacks.

    <asp:ScriptManager ID="MyScriptManager" runat="server">
    </asp:ScriptManager>

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
       <ContentTemplate>
          <asp:GridView ID="gvProducts" runat="server" AllowPaging="True"
          AllowSorting="True" AutoGenerateColumns="False" ...>
             ...
          </asp:GridView>
       </ContentTemplate>
    </asp:UpdatePanel>

    In addition to the UpdatePanel and GridView, the page includes a ScriptManager control. As discussed in the first installment of this article series, AJAX Basics and Getting Started with Microsoft's ASP.NET AJAX Framework, the ScriptManager control must be on any page that uses the ASP.NET AJAX framework, as it serves as the plumbing between the client-side and server-side aspects of an AJAX-enabled web page. As we'll see shortly, the ScriptManager control also is key in adding history points and restoring prior state.

    Visit the StandardBehavior.aspx and sort and page through the grid. Each time a sort or paging link is clicked, a partial page postback ensues and the grid's display is updated. Because partial page postbacks are used, the browser does not maintain any sort of history when sorting and paging. Therefore, if you click the Back button you will not be taken back to the previous sort order or the previous page. Instead, you'll be returned to the page you were visiting before you reached the StandardBehavior.aspx page. The following screen shots illustrate this behavior.

    Start by Visiting Default.aspx...


    Start by visiting Default.aspx...

    Click on the "Behavior With History on Sorting" Link. Now Sort and Page through the Grid Several Times. Here is the Grid After I've Sorted by Price and Moved to Page 4...


    Sort and page through the grid...

    Click Your Browser's Back Button. You are Returned to Default.aspx...


    Clicking the Back button returns you to Default.aspx...

    Adding a History Point Whenever the User Sorts the Grid


    Let's extend our example to save a history point whenever the user sorts the grid. By doing so, each time the user sorts the grid the browser will record a new history state and the browser's URL will have its querystring updated to include the state information (this allows the page to be bookmarked). With recording history points whenever the grid is sorted, the user could sort the grid by Price, then by Name, and then by ProductID. If the user, while viewing the grid sorted by ProductID, clicked his browser's Back button, he would be returned to seeing the grid sorted by Name (rather than being returned to the page visited prior to reaching this page). The demo available for download at the end of this article includes an ASP.NET page named BehaviorWithHistory.aspx that implements this functionality.

    Recall earlier that I said there are three things we need to do when using the ASP.NET AJAX history feature:

    1. Decide what actions cause a history point to be inserted.
    2. Add the history point whenever one of the actions from task #1 is performed.
    3. Create an event handler for the ScriptManager's Navigate event that restores the state when the user clicks the Back or Forward buttons, or when he reaches a page via a bookmark.
    We've already knocked out task #1 - we've decided to record the history whenever the GridView is sorted. Whenever a GridView is sorted it raises its Sorting event. We need to create an event handler for the Sorting event to add a history point.

    A history point is added via a call to the ScriptManager class's AddHistoryPoint method. This method requires that we supply a string that describes the current state. This string will be used later in the ScriptManager's Navigate event handler to reconstruct the state of the application when a page is reached via the Back or Forward buttons, or from a bookmark. Because we are basing the history point on the sort criteria, the GridView's SortExpression and SortDirection properties define the state. Therefore, we need to pass the values of these properties into the AddHistoryPoint method.

    The following code shows how I encode the GridView's state and add it as a history point.

    protected void gvProducts_Sorting(object sender, GridViewSortEventArgs e)
    {
       // Create the history state
       MyScriptManager.AddHistoryPoint("SortExpression", e.SortExpression);
       MyScriptManager.AddHistoryPoint("SortDirection", e.SortDirection.ToString());

       Page.Title = GetPageTitle(e.SortExpression, e.SortDirection);
    }

    Note that for each item I want to store in the state I call the AddHistoryPoint method, passing in a "name" for the state item and its value. In the above code I register two items in the state, named "SortExpression" and "SortDirection" and storing the values of e.SortExpression and e.SortDirection, respectively.

    After saving the state, I update the page's title. The GetPageTitle method generates and returns a suitable title given the passed-in SortExpression and SortDirection values. For instance, when sorting by the UnitPrice column in ascending order the GetPageTitle method returns the string "Viewing Products Sorted by Price (Ascending)". This value is assigned to the Page object's Title property. (For more information on how to set an ASP.NET page's title programmatically, see Dynamically Setting the Page's Title.

    If we visit the page with this code in place, each time we sort the grid the URL in the browser's Address bar is updated to include a querystring that specifies the state information. Note the Address bar in the following screen shots. When the BehaviorWithHistory.aspx page is first visited the URL is, as we'd expect, just BehaviorWithHistory.aspx.

    When first visiting BehaviorWithHistory.aspx there is no content in the querystring.

    But as soon as the grid is sorted the querystring is updated to include the encoded state information. Also the Back button is now available. In the screen shot below the grid is sorted by Price in ascending order and the URL in the Address bar now reads: BehaviorWithHistory.aspx#&&+2toRdN1TcuXKFN4BhNSqrMXMtnAEvRpZEk2MUzyrUhoPiJPO4mIypwAP9J+s7VtZ+uMZ0ub2iUXY5KDMgwufg==.

    The grid is now sorted by the Price column; the URL has been updated to include the history state in the querystring.

    While the Back button is now click-able, going back does not return us to the previous state of the grid before it was sorted by Price. Rather, we remain on the BehaviorWithHistory.aspx page, but the data is still sorted by Price. This is because we have yet to complete the final step - task #3 - which is to create an event handler for the ScriptManager control's Navigate event. The Navigate event handler fires whenever the user visits a page by clicking the Back or Forward buttons or via a bookmark. The Navigate event handler is responsible for rendering the state as specified through the querystring.

    protected void MyScriptManager_Navigate(object sender, HistoryEventArgs e)
    {
       if (!string.IsNullOrEmpty(e.State["SortExpression"]))
       {
          string sortExpressionFromState = e.State["SortExpression"];
          SortDirection sortDirectionFromState = (SortDirection)Enum.Parse(typeof(SortDirection), e.State["SortDirection"]);

          // Sort the grid according to the sort information in the history state
          gvProducts.Sort(sortExpressionFromState, sortDirectionFromState);

          Page.Title = GetPageTitle(sortExpressionFromState, sortDirectionFromState);
       }
       else
       {
          // Sort the grid by ProductName in ascending order
          gvProducts.Sort("ProductName", SortDirection.Ascending);

          Page.Title = GetPageTitle("ProductName", SortDirection.Ascending);
       }
    }

    The Navigate event handler starts by checking to see if the expected state was passed in. If so, it grabs the values from the history state (via e.State), sorts the GridView, and updates the page's title. If the state information was not passed in, it defaults to the default state - the grid sorted by the ProductName column in ascending order.

    That's all there is to it! With this code in place, the Back and Forward buttons work as expected. What's more, a user can bookmark the web page after sorting by a particular column. When the user returns to that bookmarked page, the GridView will load and automatically sort by the appropriate column.

    What About Paging?


    The BehaviorWithHistory.aspx page adds a history point whenever the user sorts the grid. It does not, however, add a history point when paging. If a user sorts the grid by, say, Price, the querystring is updated to reflect the state information. If they then go to page 2 in the grid the the querystring is not updated and no entry is added to the browser's history. If at this point they hit the Back button, they are not returned to page 1, sorted by Price. Instead, they are returned to the grid prior to sorting it by Price. Likewise, if a user sorts the grid by Price, navigates to page 5, and adds a bookmark to their browser, if they later visit that bookmark the screen will load the grid sorted by Price, but will show page 1 of the data.

    If you want to add a history point whenever the grid is sorted or paged, then you'll need to record the GridView's PageIndex property as an additional history state item in the Sorting event handler. You'll also need to create an event handler for the GridView's PageIndexChanging event, which fires whenever the user navigates to a new page. In this event handler you'll need to add a history point, recording the sort- and paging-related properties. Finally, you'll need to update the ScriptManager's Navigate event handler to set the GridView's PageIndex property to the value stored in the history state.

    The download includes a complete, working example of a GridView that records history points whenever it is sorted or pages. See the BehaviorWithHistory2.aspx ASP.NET page.

    Conclusion


    Because AJAX-enabled web pages circumvent the standard browser request/form submission model, partial page postbacks do not register as new entries in the browser's history. This behavior neuters the usefulness of the browser's Back and Forward buttons, as well as bookmarks. The good news is that the ASP.NET AJAX framework, starting with ASP.NET 3.5 SP1, includes functionality to record history points. Recording a history point saves the state that describes the history in the querystring and adds an entry to the browser's history. This enables visitors to use their browsers' Back and Forward buttons to navigate through different states in an AJAX-enabled web page. It also allows for bookmarking of different states in an AJAX-enabled application.

    Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the Demo Code Used in this Article

    A Multipart Series on ASP.NET AJAX
    Over the past several years web developers have started using JavaScript to make asynchronous postbacks to the web server that only transmit and receive the necessary data; these techniques are commonly referred to as AJAX. Microsoft has released a free AJAX framework for ASP.NET developers named Microsoft ASP.NET AJAX. This article series examines using Microsoft's ASP.NET AJAX framework to build responsive user interfaces.

  • AJAX Basics and Getting Started with Microsoft's ASP.NET AJAX Framework - examines AJAX basics and looks at installing Microsoft ASP.NET AJAX; includes a demo of the UpdatePanel control.
  • Using the UpdatePanel - provides more in-depth and real-world examples of using the UpdatePanel.
  • Providing Visual Feedback with the UpdateProgress Control - shows how to use the UpdateProgress control to display visual feedback to the user when a partial page postback is in progress.
  • Performing Client Actions in Response to Partial Page Postbacks - learn how to execute client-side script in response to events raised by the partial page postback life-cycle.
  • Using the Timer Control - see how to asynchronously update a portion of the screen every n milliseconds.
  • Enabling Bookmarking and the Browser's Back Button - learn how to add history points to an AJAX-enabled web page so that your visitors can use their browsers' Back and Forward buttons and bookmarks.
  • Retrieving Server-Side Data Using Web Services - call Web Services from an ASP.NET AJAX application with a sprinkle of JavaScript.
  • A Look at JSON Serialization - explore the serialization format used by the ASP.NET AJAX Framework when calling Web Services from client-side script.
  • Triggering Full Page Postbacks From An UpdatePanel - learn how to override an UpdatePanel's default behavior and trigger a full page postback.
  • Refreshing An UpdatePanel With JavaScript - see how to refresh the contents of an UpdatePanel via JavaScript.
  • Rebinding Client-Side Events After a Partial Page Postback - discover how to automatically rebind client-side event handlers to HTML elements in an UpdatePanel after a partial page postback.
  • (Subscribe to this Article Series! )



  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article