Cross page navigation techniques in ASP.NET 1.x and 2.0

This article discusses different ASP.Net cross page navigation mechanisms that are available in version 1.x and 2.0 and their corresponding design decisions. Five different mechanism are discussed, including client-side redirect, server-side redirect, cross-page posting, MultiView and Wizard controls. Samples for each of the mechanisms are provided as well as common design issues are discussed. 

 

Client-side redirect 1

Server-side redirect 3

Server.Transfer and Server.Execute. 3

HttpContext.RewritePath. 4

UrlMapping

Cross-page posting

MultiView control

Wizard control

Comparison table. 9

 

Client-side redirect 

To perform client side redirection in ASP.Net, users can call Response.Redirect(url). There is another overload that indicates whether the server needs to terminate the execution of the request. By default the request is terminated right away. When Response.Redirect is called, the server sends a 302 response to the client, to instruct the client to make redirected calls to the target URL. As a result, there will be a total of two requests to the server per each redirection (original request and the redirected request).

 

In this example, when button is clicked and the page is posted back to the server, the client gets redirected to new page “target.aspx” in the same server application. Note that the destination can be any arbitrary URL; it does not have to be the same application or same server.

Source.aspx

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

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

 

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e) {

        Response.Redirect("Target.aspx");

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Source Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /></div>

    </form>

</body>

</html>

 

Usually the source page needs to communicate with the target page by sending some state thru the client. Typically this can be done by using query string or server session state. See below sample that uses query string to send content of a textbox to the target page.

Source.aspx 

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

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

 

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e) {

        string targetUrl = "Target.aspx";

        targetUrl += "?text=" + TextBox1.Text;

        Response.Redirect(targetUrl);

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Source Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />

        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /></div>

    </form>

</body>

</html>

Target.aspx 

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

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

 

<script runat="server">

    protected void Page_Load(object sender, EventArgs e) {

        Label1.Text = Request.QueryString["text"];

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Target Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label ID="Label1" runat="server"></asp:Label></div>

    </form>

</body>

</html>

 

Because there is a limitation on the length of query string, it cannot be used to pass large amount of data over the wire. Alternatively, the information can be saved in the session and retrieved from the target page. However, this requires saving the information on the server, so keep in mind that it will increase server memory load.

 

Due to query string limitation and the extra server round-tripping, Client-side redirect might not be an ideal solution for certain scenarios, particularly in a Wizard like scenario (needs to send large amount of data). 

Server-side redirect

To perform server-side redirection, users can use either Server.Transfer or Server.Execute. The only difference is that Server.Transfer terminates the response immediately afterward while Server.Execute does not.

Server.Transfer and Server.Execute

In this sample, when the button is clicked and initiates a postback, the server transfers the execution to “target.aspx” on the server-side. Since the execution is transferred on the server, unlike client side redirection, it does not require another request. However, although the client users see the URL on the browser points to the original URL, it actually is handled by the target URL. If the target page postbacks to the server, then the browser URL is updated to be the target URL. Another problem with Server.Transfer is that target page cannot be bookmarked initially because it still points to the source URL.

Source.aspx

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

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

 

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e) {

        Server.Transfer("Target.aspx");

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Source Page</title>

</head>

<body>

    <form id="form1" runat="server">

<div>

    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />

        <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /></div>

    </form>

</body>

</html>

 

One improvement in ASP.Net 2.0 regarding Server.Transfer is that there is a new property on the Page called PreviousPage that basically provides a reference to the source page. Previously the reference can only be accessed by casing HttpContext.Handler to a Page type. However, if there are multiple Server.Transfer on the server, Context.Handler always point to the first page. While PreviousPage property correctly points to the immediate previous page.

 

Below is the sample that demonstrates the use of PreviousPage. Note that this page has a button, click on the button you will see the browser URL is only updated after the target page posts back.

Target.aspx

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

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

 

<script runat="server">

    protected void Page_Load(object sender, EventArgs e) {

        if (PreviousPage != null) {

            TextBox textBox = PreviousPage.FindControl("TextBox1") as TextBox;

            if (textBox != null) {

                Label1.Text = textBox.Text;

            }

        }

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Target Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label ID="Label1" runat="server" />

        <asp:Button ID="Button1" runat="server" Text="Button" />

    </div>

    </form>

</body>

</html>

 

Because the redirection happens on the server side, the target page can continue to retrieve the data directly from the source page, regardless of number of redirections happen on the server. This is the major advantage over client redirection. However, because the URL is not updated immediately, this breaks book marking. Therefore Server.Transfer is not recommended for wizard like scenario where the operations typically flow thru several different pages and book marking is required for each step.

HttpContext.RewritePath

Technically RewritePath is not a redirection, since it only executes the target URL not both URLs; it is a lower level API that is called early during server life-cycle to redirect the execution to a different handler other than the requested URL. Since a lot of people get confused with other redirection features, I just mention here for comparison. This MSDN URL Rewriting article has excellent sample and explanation of RewritePath.

 

In V1.x, there is a bug in RewritePath that would not rebase the client resource properly if the resource is placed in a different folder than the requested URL. For example, if the request URL is /app/sub/Default.aspx, and the page contains an image in /app/images/image.jpg, if the image URL is relative to the page, the server would send incorrect URL to the client for the image source. To fix this issue, there is a new overload Context.RewritePath(targetUrl, false /*rebaseClientPath*/) in V2.0 that would fix the URL rebasing issue.

UrlMapping (ASP.Net 2.0) 

UrlMapping is a new ASP.Net 2.0 feature that performs RewritePath based on config registration. See below sample web.config file.

Web.config 

<?xml version="1.0"?>

<configuration>

    <system.web>

      <urlMappings>

        <add url="~/source.aspx" mappedUrl="~/target.aspx" />

      </urlMappings>

    </system.web>

</configuration>

Cross-page posting (ASP.Net 2.0) 

Cross-page posting is a new ASP.Net 2.0 feature that combines the benefits of client and server side redirection with the exception that the target URL has to be pre-determined. In the case of Client or Server redirect, the target can be dynamically changed on the server-side. The URL of the target is reflected correctly on the browser; on the server, there is a PreviousPage property that allows strongly-typed reference to retrieve data from the previous page.

 

To use cross-page posting, set PostBackUrl on any controls that implement IButtonControl, which includes Button, ImageButton and LinkButton. Before postback happens on the client, the client side scripts changes the form’s action attribute to the PostBackUrl and the page postbacks to the target URL. The client also sends a special encrypted hidden field, __PREVIOUSPAGE, to the server which indicates the previous page identity. When the server receives the request to the target page, it recognizes this is a cross-page postback because of the hidden field, and it creates the PreviousPage on demand and lets the previous page handles the viewstate and request.

 

Note that because PreviousPage is created on demand, the previous page runs thru the complete life-cycle until PreRender when it is instantiated. This means the events on the PreviousPage might be fired at different time compared to the target page, depending on when PreviousPage property is accessed.

Source.aspx 

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

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

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Source Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:TextBox ID="TextBox1" runat="server" /><br />

        <asp:Button ID="Button1" runat="server" PostBackUrl="Target.aspx" Text="Button" />

    </div>

    </form>

</body>

</html>

Target.aspx 

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

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

 

<script runat="server">

    protected void Page_Load(object sender, EventArgs e) {

        if (PreviousPage != null) {

            TextBox textBox = PreviousPage.FindControl("TextBox1") as TextBox;

            if (textBox != null) {

                Label1.Text = textBox.Text;

            }

        }

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Target Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label ID="Label1" runat="server" />

        <asp:Button ID="Button1" runat="server" Text="Button" />

    </div>

    </form>

</body>

</html>

 

Note that unlike Server.Transfer where you can access any previous page or their ancestor previous pages. You can only access the immediate previous page if the pages are cross-page posted several times. If you need to save the data from multiple pages, you can save it either in the ViewState or server-side Session object. Typically ViewState is a better option since it does not increase server memory load, however, there is still a limitation on the size of hidden field in the browser, which limit the amount of ViewState you can save.

 

Other useful references:

Rob Howard's Page Navigation Article

MSDN Cross-Page Posting Article

MultiView control (ASP.Net 2.0) 

MultiView is a new ASP.Net 2.0 control that supports Wizard-like scenarios. A MultiView controls contains one or more View controls, only one of the View controls will be the active control at a time, and only the active View will be rendered. The client only interacts with one of the View controls at a time, and the server decides which View control to be displayed based on user input. MultiView controls also make sure only validators within the current view are active, other validators will not validate on a postback. Since the page simply posts back to the same page with active view changes between View controls, the benefit is the ability to gather all ViewState changes easily regardless of number of postbacks. In addition, since controls are all defined on the same page, it is easy to find controls and their associated object models.

 

In a wizard like scenario, it is uncommon for client to be able to bookmark individual views or navigating directly to any view directly. However, if users wish to support this, there are couple possible solutions: 1. Change the form method to get. This would preserve url and form variables, including the ViewState, therefore this approach works only if the viewstate is small; unfortunately this is not true in most cases. 2) Change the MultiView control navigation logic so it is based on a query string, the server makes sure the query string always points to the current active view.

 

The first sample MultiView.aspx demonstrates a very simple MultiView usage. Initially the first view (View1) is displayed. When the button postbacks to the server, the second view (View2) becomes the active view control by programmatically changing the ActiveViewIndex to 1. The second sample, MultiView2.aspx demonstrates how bookmarking can be done on a MultiView control.

MultiView.aspx 

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

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

 

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e) {

        MultiView1.ActiveViewIndex = 1;

    }

 

    protected void View2_Activate(object sender, EventArgs e) {

        Label1.Text = TextBox1.Text;

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>MultiView sample page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">

            <asp:View ID="View1" runat="server">

                <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />

                <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />

            </asp:View>

            <asp:View ID="View2" runat="server" OnActivate="View2_Activate">

                <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

            </asp:View>

        </asp:MultiView></div>

    </form>

</body>

</html> 

MultiView2.aspx 

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

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

 

<script runat="server">

    protected void Page_Init(object sender, EventArgs e) {

        if (Request.QueryString["ActiveView"] != null) {

            MultiView1.ActiveViewIndex = Int32.Parse(Request.QueryString["ActiveView"]);

        }

    }

 

    protected void Page_PreRender(object sender, EventArgs e) {

        Label1.Text = "Current View is " + MultiView1.ActiveViewIndex;

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>MultiView BookMarking Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label><br />

        <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">

            <asp:View ID="View1" runat="server">

                <asp:Button ID="Button1" runat="server" CommandName="NextView" Text="Next View" PostBackUrl="?ActiveView=1"/>

            </asp:View>

            <asp:View ID="View2" runat="server">

                <asp:Button ID="Button2" runat="server" CommandName="PrevView" Text="Previous View" PostBackUrl="?ActiveView=0"/>           

                <asp:Button ID="Button3" runat="server" CommandName="NextView" Text="Next View" PostBackUrl="?ActiveView=2"/>

            </asp:View>

            <asp:View ID="View3" runat="server">

                <asp:Button ID="Button4" runat="server" CommandName="PrevView" Text="Previous View" PostBackUrl="?ActiveView=2"/>

            </asp:View>

        </asp:MultiView></div>

    </form>

</body>

</html>

 

Other useful resources:

MSDN MultiView control article

Wizard control (ASP.Net 2.0) 

Because a MultiView control is just a container control, it does not provide any rich UI or style customization abilities. Wizard control, on the other hand, provides richer WebControl features. Think of Wizard control as a more advanced MultiView, the differences are:

1.      Wizard derives from System.Web.UI.WebControls.WebControl, therefore it supports Style and richer set of API.

2.      Wizard provides a default SideBar, which allows client to navigate to any steps; this is typically useful in a non-linear navigation scenario.

3.      Wizard provides templating ability; the control provides default templates for several of the internal areas, including the SideBar. Each of these templates can be customized thru template properties.

4.      Richer design-time support – create/delete steps, convert current styles to templates.

5.      The ability to block navigating back to previous steps based on server-side logic.

6.      MultiView is a lightweight control that simply act like a container

 

Below is a page that uses a simple Wizard control to collect data from first step and display the content on the second step.

 

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

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

 

<script runat="server">

    protected void Page_Load(object sender, EventArgs e) {

        Label1.Text = TextBox1.Text;

    }

</script>

 

<html xmlns="https://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Wizard Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Wizard ID="Wizard1" runat="server">

            <WizardSteps>

                <asp:WizardStep runat="server" Title="Step 1">

                    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

                </asp:WizardStep>

                <asp:WizardStep runat="server" Title="Step 2">

                    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

                </asp:WizardStep>

            </WizardSteps>

        </asp:Wizard>

    </div>

    </form>

</body>

</html>

 

Other useful resources:

MSDN Wizard control article

Comparison table 

 

ASP.Net version

technology

Retrieve state from previous page

Bookmarking

Client redirect

1.0 +

Client-side

Bad (use querystring)

Easy

Server redirect

1.0 +

Server-side

Good (use PreviousPage)

Bad

RewritePath

1.0 +

Server-side

Bad

Bad

Cross page posting

2.0

Mixed

Good (use PreviousPage)

Easy

MultiView

2.0

Server-side

Excellent

Difficult

Wizard

2.0

Server-side

Excellent

Difficult