Extreme ASP.NET

Page Navigation

Rob Howard

Contents

Cross-Page Posting
Wizard Control
Conclusion

In my childhood I spent several weeks a year in Holland with my extended family. As a young American boy I was fascinated with the electric Dutch trains, something we didn't see in my hometown of Dallas, Texas. My cousins entertained me by taking me out in their boats to watch the trains go by. Sitting in the water near the tracks you could hear the approaching train as a slight steely whisper reverberating through the rails, building slowly to a crescendo as the train rushed past. Thinking about ASP.NET 2.0 reminds me of that approaching train. It's nearly here and many of us eagerly anticipate the release. We can already hear the hum, which will only continue to get louder until the release. Then the way we write software will change, yet again.

The Microsoft goal for ASP.NET 2.0 was to increase developer productivity by 50 percent. However, the true productivity gains look to be exceeding that goal. New personalization, membership, and role management features take the burden off the developer, and other features, like data binding, have been simplified. For example, while this familiar syntax is still supported:

<%# DataBinder.Eval (Container.DataItem, "FirstName") %>

In ASP.NET 2.0 it can now be shorted to:

<%# Eval("FirstName") %>

While the number of new features is impressive, the number of new server controls is significant as well. With server controls such as the <asp:login> control for integrating with membership and the new data source and data controls server controls, the ASP.NET declarative programming model becomes much more powerful in ASP.NET 2.0.

In ASP.NET 2.0 the number of class libraries for System.Web nearly doubles—too much to cover in even a series of magazine columns. To truly understand the extent of the changes, you'll need one of the new ASP.NET 2.0 books. What I plan to do here is present a series of columns to highlight some of the great new features that are available with ASP.NET 2.0. This month I'll focus on navigation and page flow starting with a feature many have asked for—the ability to post to another page.

Cross-Page Posting

One of the biggest gripes I hear from developers who are new to ASP.NET is the frustration with the page postback model. An ASP.NET page can have a single <form> element and can only perform an HTTP postback to itself, thus all processing logic runs in the same page with the UI code.

Many developers, especially those familiar with ASP, enjoyed granular control over the <form> element. You could dictate where the <form> sent its contents, how it sent its contents (HTTP Post or HTTP Get), and the number of <form> elements on a single page. In contrast to ASP, ASP.NET only allows a single <form runat=server> element on a page and it can only postback to itself. This can have annoying consequences. Here's an example in which the user is going to be sent to another page:

<%@ Page MasterPageFile="~/Site.master" Language="C#" 
    CodeFile="Source.aspx.cs" 
    Inherits="Source" %>

<asp:Content ID="MainContent" 
        ContentPlaceHolderID="Main" Runat="server">
    Enter your name:
    <asp:TextBox ID="NameBox" Runat="server"></asp:TextBox>
    <asp:Button ID="Button1" Runat="server" Text="Submit" />
</asp:Content>

In this example Master Pages are used to control where the content is placed in the page layout. Within an <asp:content> region there are some server controls for capturing input from a user.

If the desired effect was to pass this content to another page you might see something like this in the server code:

Response.Redirect("Target.aspx?Name= " + 
    HttpUtility.UrlEncode(NameBox.Text));

The problem with this technique is that it causes the user to click the button to submit the page, resulting in the server processing the request, sending a response back to the browser requesting that the browser go to Target.aspx. That is quite a lot of work for a very simple problem!

Wouldn't it just be easier to post to Target.aspx? In ASP.NET 2.0 you can now do just that. The following shows the same code modified slightly:

<%@ Page MasterPageFile="~/Site.master" Language="C#" 
    CodeFile="Source.aspx.cs" 
    Inherits="Source" %>

<asp:Content ID="MainContent" 
        ContentPlaceHolderID="Main" Runat="server">
    Enter your name:
    <asp:TextBox ID="NameBox" Runat="server"></asp:TextBox>
    <asp:Button ID="Button1" Runat="server" Text="Submit" 
        PostBackUrl="~/Target.aspx" />
</asp:Content>

Note the new PostBackUrl property in the <asp:Button> field. This property tells the button that instead of performing a standard postback the page posts directly to Target.aspx.

You're probably wondering how this all works, especially if you're familiar with the ASP.NET ViewState object. Without going into detail that is beyond the scope of this column, there is a new hidden <form> element added to the page when the cross-page posting feature is used:

<input type="hidden" name="__PREVIOUSPAGE" id="__PREVIOUSPAGE" 
    value="p1-dFHlCpgH2alr1vkr3G21UIR7jOuzn074led6lbGf1KQ47_F25GwG0" />

This is similar to the view state data generated by the control tree, but it's a fragment of view state related to cross-page posting to validate the page. You see, when a page is cross-page posted the page that is posted to has access to an instance of the page posted from. In the example it means that in Target.aspx you can access details of Source.aspx. In fact, with a little more effort you can access the APIs of Source.aspx from Target.aspx in a strongly typed manner. In order to access the page posted from (the previous page), there is a new Page property in ASP.NET 2.0 specifically for cross-page posting, which is aptly named PreviousPage.

PreviousPage returns an instance of the page posted from. There is also a property on the object returned from PreviousPage for checking whether there was a cross-page postback: IsCrossPagePostBack. This property works just like IsPostBack but returns true when a cross-page postback has occurred.

The PreviousPage property can behave differently. By default it will simply return an instance of the previous page as type Page. But, by using a new directive you can have the PreviousPage property return a strongly typed instance, allowing access to public members of that page. For example, the following could be added to Target.aspx:

<%@ PreviousPageType VirtualPath="~/Source.aspx"  %>

Now the PreviousPage property can be used from Target.aspx to access data on Source.aspx. However, to access server controls, such as the NameBox on Source.aspx, you would still need to write code similar to this:

TextBox nameBox = PreviousPage.FindControl("NameBox") as TextBox;

In other words, you would have to access the control tree using FindControl. Why? Server controls within a page are by default protected member variables. To truly simplify access to elements on the previous page, you would expose public properties or methods on Source.aspx so the following would work:

TextBox nameBox = PreviousPage.NameBox;

Cross-page posting is a great new feature of ASP.NET 2.0. There already are some articles that discuss cross-page posting in more technical detail if you would like to learn more about how it works (see Dino Esposito's Cutting Edge column in the September 2005 issue of MSDN®Magazine). You will also likely find, if you are already well versed in ASP.NET, that you will continue to use the standard page postback model most often.

Wizard Control

Using cross-page posting, you can more easily build complex navigation within your application. However, even this feature doesn't make it any easier to build wizard-style UI. For performing tasks, whether linear or nonlinear, wizard-style UI is often desired. It provides the end user with a familiar path through what can be a complex series of steps with each step broken into smaller chunks.

In ASP.NET 1.x, wizards were often implemented using a technique common to many Web-based wizard UIs: a series of <asp:panel> server controls within a page that are selectively turned on and off depending upon where the user is within the page. Internally the wizard state is tracked to determine which step the user is on, which step is next, which step is previous, and when the user has provided enough information to finish. Simply put, writing a wizard UI in ASP.NET 1.1 is not easy; much of the Visual Studio® designer capabilities are lost and the logic of managing the flow of the steps can be confusing.

The new cross-page post capabilities of ASP.NET 2.0 could be used to solve the wizard problem, but would be equally as challenging when the need for nonlinear navigation exists, for example, Step 1, Step 2, skip Steps 3—5, Step 6, Step 3, Step 2, Step 6. The new ASP.NET 2.0 Wizard control solves much of this. Additionally, by using the page postback model instead of cross-page posting all input elements of the wizard are accessible continuously.

The Wizard control largely resembles functionality available today in ASP.NET 1.1 through the use of hidden panels. However, the new control exposes the panels as a series of <asp:WizardStep> elements which contain any number of controls. There is no limit to the number of <asp:WizardStep> children; however each <asp:WizardStep> needs its own unique ID, as you can see in Figure 1. The new Wizard control also manages all navigation, supporting both linear and nonlinear navigation, and has full support for the Visual Studio design environment. Figure 2 shows the Wizard control navigation elements, including nonlinear link-based navigation on the left and linear button navigation on the bottom right. The common tasks menu in Visual Studio is also open, showing not only common tasks for the Wizard but a dropdown list of steps allowing you to switch between design modes of various steps.

Figure 1 Wizard Steps

<asp:Wizard runat="server" >
  <WizardSteps>

    <asp:WizardStep ID="Step1">
       Welcome!
    </asp:WizardStep>

    <asp:WizardStep ID="Step2">
      What is your name: [TextBox1]
      [Button1]
    </asp:WizardStep>

    <asp:WizardStep ID="Step3">
      Thank you, [TextBox1.Text]!
    </asp:WizardStep>

  </WizardSteps>
</asp:Wizard>

Figure 2 Wizards in Visual Studio

Figure 2** Wizards in Visual Studio **

All of the visual elements within the Wizard control are configurable. The nonlinear links can be replaced with buttons or removed entirely, and the Previous, Next, and Finish linear navigation elements can also be changed to image buttons or links. In fact, all aspects of the control are configurable through templates.

One challenge of writing a Wizard control in ASP.NET 1.1 was managing where the user was within the wizard. The Wizard control simplifies this by exposing an ActiveStep property. The ActiveStep property can be queried to determine which step is currently active. The natural flow of the Wizard will be the order in which the steps are declared; however, this flow can be altered at any time using the Wizard's MoveTo method. Using MoveTo, any step can be made the ActiveStep. To help with the navigation and flow there are also several events, which are shown in Figure 3.

Figure 3 Navigation Events

Event Description
ActiveStepChanged Raised when the ActiveStep is set to a new WizardStep
CancelButtonClick Raised when the button identified as the Cancel button is clicked
FinishButtonClick Raised when the button identified as the Finish button is clicked
NextButtonClick Raised when the button identified as the Next button is clicked
PreviousButtonClick Raised when the button identified as Previous button is clicked
SideBarButtonClick Raised when one of the SideBar links or buttons is clicked

The new Wizard control is exceedingly useful for collecting information from users. Unlike ASP.NET 1.1 where all the infrastructure of the wizard UI had to be written, this is all done for you in ASP.NET 2.0. The Wizard control is so useful that the ASP.NET team used it as the base class for CreateUserWizard control. The CreateUserWizard control is used as part of the Membership functionality for creating new users.

Conclusion

Cross-page posting and the <asp:Wizard> control give ASP.NET developers several new options for control navigation flow within their application. Cross-page posting is most useful for cases where a Response.Redirect or Server.Transfer is used today. The Wizard control is great for building complex data collection that requires both linear and nonlinear navigation.

Next time I'll dissect URL rewriting and take a look at the new site map feature in ASP.NET 2.0.

Send your questions and comments for Rob to  xtrmasp@microsoft.com.

Rob Howard is a founder of Telligent Systems, specializing in high-performance Web apps, knowledge management, and collaboration systems. Previously, Rob was employed by Microsoft where he helped design the infrastructure features of ASP .NET 1.0, 1.1,and 2.0. You can contact Rob at rhoward@telligentsystems.com.