Export (0) Print
Expand All

Managing Browser History Using ASP.NET Server Controls

As a page developer, you can manage browser history entries and provide logical navigation by using the ScriptManager and ScriptManagerProxy server controls. The ScriptManager server control lets you set history points in the application. Both controls let you handle the navigation that occurs when the Web page is requested as a result of some history state.

A history point is a logical navigation point in the Web application that can be represented through state information. The state information can be used to restore the Web application to a previous state, either directly with state data or through an identifier for state information that is stored elsewhere.

History points are stored in the browser's history stack only as URLs. History state is managed as data in a query string or as a URL fragment value that is marked with the "#" character. Because of size restrictions on URLs, the state information that you create must be as small as possible. The following example shows a URL that contains enough history-point data to identify the state. From this, the application can re-create the page at that state.

http://MySamples/Ajax/Default.aspx#&&state=2

When a user clicks the browser's Back button, the browser moves through previously-viewed URLs, which includes URLs that contain history point state. Client code in the Web page detects that the URL contains history state data and makes a request to the page, passing the state information. When the page processes the request, it reads the history state information and triggers an asynchronous postback. The ScriptManager and ScriptManagerProxy server controls then raise the Navigate event. You can handle this event and re-create the page as required for your Web application.

NoteNote:

To build the example code in this topic, you must have Visual Studio 2008 Service Pack 1 or a later version.

The following example shows the syntax of the ScriptManager server control for working with browser history.

<asp:ScriptManager runat="server" 
    EnableHistory="true|false"
    EnableStateHash="true|false"
    OnNavigate="navigateEventhandlerName">
</asp:ScriptManager>

In order to use history management, you must enable it through the ScriptManager server control. By default, history support is not enabled. When history is enabled, it is implemented differently for each browser. For Internet Explorer, an iframe element is rendered to the browser, which can cause an additional request to the server. The model is therefore an opt-in approach. The following example shows how to enable history declaratively through the ScriptManager control.

<asp:ScriptManager runat="server" ID="ScriptManager1" 
    EnableHistory="true" />

To create a browser history point, you call the AddHistoryPoint method of the ScriptManager control. This method enables you to define server state and keys, and optional data to represent the title of the history entry in the browser. You can use the state data to re-create the state of the page when a subsequent history navigation event is raised. When you create a history point, by default serialized and encrypted data is appended to the URL of the Web page. The resulting URL is included in the browser's history stack.

NoteNote:

Add history points only in response to user actions, such as clicking a selection. Typically, history points are not added as a result only of running application code.

The following example uses the UpdatePanel control to enable asynchronous postbacks on the page. The ScriptManager control is used to add history points during the Click event handler of the buttons that trigger the asynchronous postbacks. When you click the browser's Back button, you do not leave the Web page, but instead navigate through the page's previous history states.

See a run-time code example of this feature: Run.

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Globalization" %>
<%@ Import Namespace="System.Collections.Generic" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    private static String key = "s";

    // Handle the Navigate event
    public void OnNavigateHistory(object sender, HistoryEventArgs e) {
        LabelHistoryData.Text = Server.HtmlEncode(e.State[key]);
    }

    // On button click, handle the event and set a history point. 
    public void ButtonClick(object sender, EventArgs e) {
        LabelHistoryData.Text = ((Button)sender).Text;
        ScriptManager.GetCurrent(this).AddHistoryPoint(key, LabelHistoryData.Text);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Microsoft ASP.NET 3.5: Managing History</title>
    <link href="../../include/qsstyle.css" type="text/css" rel="Stylesheet" />
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:ScriptManager runat="server" ID="ScriptManager1" OnNavigate="OnNavigateHistory" 
                EnableHistory="true" EnableSecureHistoryState="false" />
            <h2>
                Adding Server-side Browser History Points</h2>
            <p/>

            <div id="Div1" class="new">
                <p>This sample shows:</p>
                <ol>
                    <li>How to use the <code>ScriptManager</code> control to set a history point.</li>
                    <li>The <code>ScriptManager</code> control, the <code>EnableHistory</code> and 
                    <code>EnableSecureHistoryState</code> properties and 
                    the <code>OnNavigate</code> property to handle the <code>navigate</code>event.<br />
                    </li>
                </ol>
            </div>

            <p>
                In this example, three buttons outside the <code>UpdatePanel</code> control can
                trigger an asynchronous postback. You can see the results of the update through
                the date and time that renders in the Web page along with a value indicating the 
                button that triggered the partial refresh.</p>
            <p>
                When you press a button, the server-side <code>Click</code> event handler for the button 
                stores data and uses the data as a history point. If you click the browser's Back button, 
                you will see the state Web page return to a previous button value, representing the previous 
                button you pressed. However, you will see that the time within the Web page continues to be 
                incremented.
            </p>
            <p>To see the effect of this logic, do the following.</p>

            <ol>
                <li>Press <b>1</b>. See the panel refresh.</li>
                <li>Press <b>3</b>. See the panel refresh.</li>
                <li>Press <b>2</b>. See the panel refresh.</li>
                <li>Press the browser's Back button. Note that the panel is refreshed with previous
                    data, but the date and time are updated to current values.</li>
            </ol>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <Triggers>
                    <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button2" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button3" EventName="Click" />
                </Triggers>
                <ContentTemplate>
                    <asp:Panel runat="server" CssClass="box" ID="Content" Height="40px">
                        Date and Time:
                        <%= DateTime.Now.ToLongTimeString() %>
                        <br />
                        Page's refresh state:
                        <asp:Label runat="server" ID="LabelHistoryData" />
                    </asp:Panel>
                </ContentTemplate>
            </asp:UpdatePanel>
            <p />
            <asp:Button runat="server" ID="Button1" Text="Key 1" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button2" Text="Key 2" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button3" Text="Key 3" OnClick="ButtonClick" />
        </div>
    </form>
</body>
</html>


When server state is detected in a request, the Navigate event is raised. This appears like an asynchronous postback to the server. If you have to determine whether the postback occurred for navigation or for some other purpose, you can read the IsNavigating property. If this property is set to true, the postback originated as a navigation call.

The following example shows the Wizard server control inside an UpdatePanel control. This causes the Wizard control to perform asynchronous postbacks when users navigate in the wizard. In the example, code adds history points when the steps in the wizard are navigated.

See a run-time code example of this feature: Run.

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    private static readonly string stepKey = "s";

    protected void OnNavigateHistory(object sender, HistoryEventArgs args)
    {
        string stateString = args.State[stepKey];
        int step = (stateString != null) ? int.Parse(stateString) : 0;
        MachineConfiguratorWizard.ActiveStepIndex = step;
    }

    protected void OnActiveStepChanged(object sender, EventArgs e)
    {
        if (!ScriptManager1.IsNavigating && IsPostBack) {
            int index = MachineConfiguratorWizard.ActiveStepIndex;
            ScriptManager1.AddHistoryPoint(stepKey, index.ToString(), "Step " + (index+1).ToString());
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Microsoft ASP.NET 3.5: Managing History</title>
    <link href="../../include/qsstyle.css" type="text/css" rel="Stylesheet" />
</head>
<body>
    <div>
    <form id="form1" runat="server">
        <div>
            <asp:ScriptManager runat="server" ID="ScriptManager1" OnNavigate="OnNavigateHistory" 
                EnableHistory="true" EnableSecureHistoryState="false" />
            <h2>
                Adding Server-side Browser History Points</h2>
            <p/>

            <div id="Div1" class="new">
                <p>This sample shows:</p>
                <ol>
                    <li>How to use the <code>ScriptManager</code> control to set a history point.</li>
                    <li>The <code>ScriptManager</code> control, the <code>EnableHistory</code> and 
                    <code>EnableSecureHistoryState</code> properties and 
                    the <code>OnNavigate</code> property to handle the <code>navigate</code> event.
                    </li>
                    <li>Protecting the history code with <code>IsNavigating</code>
                    </li>
                </ol>
            </div>
        <p>
        In this example, the <code>Wizard</code> server control provides it's own navigation, but 
        as each step is selected a history point is added. In order to do this, a history point is only added if the page is not being refreshed beacuse of a history point.</p>
            <asp:UpdatePanel runat="server" ID="WizardPanel">
                <ContentTemplate>
                    <asp:Wizard ID="MachineConfiguratorWizard" runat="server" ActiveStepIndex="0" BackColor="#dddddd"
                        BorderWidth="10" CellPadding="10" CellSpacing="10" Height="200px" Width="700px"
                        FinishPreviousButtonText="<" StartNextButtonText=">" StepNextButtonText=">" StepPreviousButtonText="<"
                        FinishCompleteButtonText="<|>" OnActiveStepChanged="OnActiveStepChanged">
                        <WizardSteps>
                            <asp:WizardStep ID="Step1" runat="server" Title="Step 1">
                                <h2>
                                    STEP 1</h2>
                                <br />
                            </asp:WizardStep>
                            <asp:WizardStep ID="Step2" runat="server" Title="Step 2">
                                <h2>
                                    STEP 2</h2>
                                <br />
                            </asp:WizardStep>
                            <asp:WizardStep ID="Step3" runat="server" Title="Step 3">
                                <h2>
                                    STEP 3</h2>
                                <br />
                            </asp:WizardStep>
                        </WizardSteps>
                        <StepStyle Font-Names="tahoma" Font-Size="Smaller" VerticalAlign="Top" />
                        <SideBarStyle Font-Size="Small" VerticalAlign="Top" BackColor="#FFFFC0" Font-Names="tahoma" />
                        <FinishPreviousButtonStyle BackColor="White" BorderColor="Black" BorderWidth="3px"
                            Font-Names="Tahoma" Font-Size="Medium" />
                        <NavigationButtonStyle BackColor="White" BorderColor="Black" BorderStyle="Solid"
                            BorderWidth="3px" Font-Names="Tahoma" Font-Size="Medium" />
                        <FinishCompleteButtonStyle Font-Names="Tahoma" Font-Size="Medium" />
                    </asp:Wizard>
                </ContentTemplate>
            </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>


When you create a history point, you decide what information to store for the history-point state. As a minimum, you must use a key/value pair that identifies the state to your application. You might decide to store additional key/value pairs. However, because this information is stored in the URL and the URL has a size limit (as determined by the browser), store only the information that you require in order to re-create the state.

In the URL, the server state information can be either in a hashed form or unhashed form, depending on the setting you define for the EnableHistory property of the ScriptManager server control.

History state is delimited in the URL with the # character (fragment delimiter). State information follows the "&&" separator, as shown in the following example:

Default.aspx#&&s=Key+2

When you set EnableHistory to true, the history state fragment is encrypted before it is appended to the URL of the Web page. This improves security by making it difficult for an attacker to change the state data. However, even though the information is encrypted, do not store sensitive data in the state field.

Note that hashing state information creates URLs that are lengthy and that include information that is not meaningful to the user.

Entries in the browser's history stack are typically identified by the title of the page for that entry. To see an example of this, use your browser's history list to view the titles of pages that you visited recently. (You can usually see the list by using a drop-down list in the URL address box.)

When you create a history point entry in your application, by default the page's title is used to identify the entry. If you add multiple history points from the same page, by default all the entries will have the same title.

However, you can provide meaningful titles for individual history entries. In server code, you can include title information when you create a history point by calling the AddHistoryPoint method.

The following example shows a variation on the previous example by including a title entry.

See a run-time code example of this feature: Run.

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Globalization" %>
<%@ Import Namespace="System.Collections.Generic" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    private static String key = "s";

    // Handle the Navigate event
    public void OnNavigateHistory(object sender, HistoryEventArgs e) {
        LabelHistoryData.Text = Server.HtmlEncode(e.State[key]);
    }

    // On button click, handle the event and set a history point. 
    public void ButtonClick(object sender, EventArgs e) {
        LabelHistoryData.Text = ((Button)sender).Text;
        ScriptManager.GetCurrent(this).AddHistoryPoint(key, LabelHistoryData.Text, "Entry: " + LabelHistoryData.Text);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Microsoft ASP.NET 3.5: Managing History</title>
    <link href="../../include/qsstyle.css" type="text/css" rel="Stylesheet" />
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:ScriptManager runat="server" ID="ScriptManager1" OnNavigate="OnNavigateHistory" 
                EnableHistory="true" EnableSecureHistoryState="false" />
            <h2>
                Adding Server-side Browser History Points and Title Entries</h2>
            <p/>

            <div id="Div1" class="new">
                <p>This sample shows:</p>
                <ol>
                    <li>How to use the <code>ScriptManager</code> control to set a history point and add titles.</li>
                    <li>The <code>ScriptManager</code> control, the <code>EnableHistory</code> and 
                    <code>EnableSecureHistoryState</code> properties and 
                    the <code>OnNavigate</code> property to handle the <code>navigate</code>event.<br />
                    </li>
                </ol>
            </div>

            <p>
                In this example, three buttons outside the <code>UpdatePanel</code> control can
                trigger an asynchronous postback. You can see the results of the update through
                the date and time that renders in the Web page along with a value indicating the 
                button that triggered the partial refresh.</p>
            <p>
                When you press a button, the server-side <code>Click</code> event handler for the button 
                stores data and uses the data as a history point. If you click the browser's Back button, 
                you will see the state Web page return to a previous button value, representing the previous 
                button you pressed. However, you will see that the time within the Web page continues to be 
                incremented.
            </p>
            <p>To see the effect of this logic, do the following.</p>

            <ol>
                <li>Press <b>1</b>. See the panel refresh.</li>
                <li>Press <b>3</b>. See the panel refresh.</li>
                <li>Press <b>2</b>. See the panel refresh.</li>
                <li>Press the browser's Back button. Note that the panel is refreshed with previous
                    data, but the date and time are updated to current values.</li>
                <li>Press the browser's "Recent Pages" drop down menu and review the history entries and their titles.</li>
            </ol>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <Triggers>
                    <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button2" EventName="Click" />
                    <asp:AsyncPostBackTrigger ControlID="Button3" EventName="Click" />
                </Triggers>
                <ContentTemplate>
                    <asp:Panel runat="server" CssClass="box" ID="Content" Height="40px">
                        Date and Time:
                        <%= DateTime.Now.ToLongTimeString() %>
                        <br />
                        Page's refresh state:
                        <asp:Label runat="server" ID="LabelHistoryData" />
                    </asp:Panel>
                </ContentTemplate>
            </asp:UpdatePanel>
            <p />
            <asp:Button runat="server" ID="Button1" Text="Key 1" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button2" Text="Key 2" OnClick="ButtonClick" />
            <asp:Button runat="server" ID="Button3" Text="Key 3" OnClick="ButtonClick" />
        </div>
    </form>
</body>
</html>


Community Additions

ADD
Show:
© 2014 Microsoft