Hands-on Navigation for ASP.NET Web Forms - Part 1





3.00/5 (2 votes)
Implementation of a Web UI Wizard using the Navigation for ASP.NET Web Forms framework
Introduction
This article illustrates how to implement a Web UI Wizard using the open source Navigation for ASP.NET Web Forms project, located at http://navigation.codeplex.com. The standard ASP.NET approach would be to use a Wizard control and define the WizardSteps. This can lead to one large, unmaintainable aspx Page. The Navigation for ASP.NET Web Forms framework allows each step to be its own aspx Page. The framework manages all the navigation and data passing between these steps. This leads to small, maintainable aspx pages with (almost) empty code-behinds.
Background
Navigation for ASP.NET Web Forms is an open source project, located at http://navigation.codeplex.com, that handles navigation and data passing between aspx pages. This article goes straight into using this framework without explaining it and so assumes a familiarity with the project's documentation.
Using the Code
This article walks through the creation of a Wizard navigation with three steps. The first step collects the user's title and gender; the second step collects the user's name and age indicator; and the third step summarises the user's selections. The sample code is written in VS 2008, although the Navigation for ASP.NET Web Forms project works in VS 2005 or later - the relevant binary for the version of the .NET framework in use can be downloaded from http://navigation.codeplex.com.
Start by creating a new Web Site called WizardNavigation and set it up to use the Navigation for ASP.NET Web Forms framework by registering the StateAdapter
class as an adapter for the Page control (see the Getting Started section of http://navigation.codeplex.com/documentation).
Next the Dialogs, States and Transitions, that make up the State Information configuration, must be defined. Each step represents a separate State
, since each is its own aspx Page, and these will be called Step1
, Step2
and Summary
. The transitions are straightforward, as each step must be able to Navigate to the next step, and so there is one from Step1
to Step2
and one from Step2
to Summary
. This configuration is shown below:
<dialog key="Wizard" initial="Step1" path="~/Step1.aspx">
<state key="Step1" page="~/Step1.aspx" title="Step 1">
<transition key="Next" to="Step2"/>
</state>
<state key="Step2" page="~/Step2.aspx" title="Step 2">
<transition key="Next" to="Summary"/>
</state>
<state key="Summary" page="~/Summary.aspx" title="Summary">
</state>
</dialog>
To the Web Site, add a Page called Step1.aspx. To Step1.aspx, add a FormView
bound to a NavigationDataSource
(see the Data Binding section of http://navigation.codeplex.com/documentation) and containing a Title DropDownList
and a Gender RadioButtonList
(both with an associated RequiredFieldValidator
) and an Update Button. The SelectedValue
properties of the DropDownList
and RadioButtonList
are set using the 2-way databinding syntax. The code for Step1.aspx is shown below:
<asp:FormView ID="FormView1" runat="server"
DataSourceID="NavigationDataSource1" DefaultMode="Edit">
<EditItemTemplate>
<div><asp:Label ID="Label1" runat="server"
Text="Title" AssociatedControlID="DropDownList1" />
<asp:DropDownList ID="DropDownList1" runat="server"
SelectedValue="<%# Bind('[title]') %>">
<asp:ListItem Text="" />
<asp:ListItem Text="Mr" />
<asp:ListItem Text="Ms" />
<asp:ListItem Text="Miss" />
<asp:ListItem Text="Mrs" />
</asp:DropDownList>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
runat="server" ControlToValidate="DropDownList1"
EnableClientScript="False"
ErrorMessage="Please select a Title" /></div>
<div><asp:Label ID="Label2" runat="server"
Text="Gender" AssociatedControlID="RadioButtonList1" />
<asp:RadioButtonList ID="RadioButtonList1"
runat="server" SelectedValue="<%# Bind('[gender]') %>"
RepeatDirection="Horizontal">
<asp:ListItem Text="Male" />
<asp:ListItem Text="Female" />
<asp:ListItem Text="Blank" />
</asp:RadioButtonList>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2"
runat="server" ControlToValidate="RadioButtonList1"
EnableClientScript="False"
ErrorMessage="Please select a Gender" InitialValue="Blank" />
</div>
<asp:Button ID="Button1" runat="server" Text="Next"
CommandName="Update" />
</EditItemTemplate>
</asp:FormView>
<cc1:NavigationDataSource ID="NavigationDataSource1" runat="server">
</cc1:NavigationDataSource>
Setting Step1.aspx as the start page and pressing F5 will result in the error "RadioButtonList1
has a SelectedValue
which is invalid because it does not exist in the list of items." This is because the SelectedValue
is databound to the StateContext
Data item with key 'gender
' which starts out with a null
value which, as the error says, is not in the list of items. The SelectParameters
of the NavigationDataSource
can fix this as these are used to initialize StateContext
Data items. So, to initialize the StateContext
Data item with key 'gender
' to a valid list item value, add the SelectParameter
shown below:
<cc1:NavigationDataSource ID="NavigationDataSource1" runat="server">
<SelectParameters>
<cc1:NavigationDataParameter Name="gender" DefaultValue="Blank"/>
</SelectParameters>
</cc1:NavigationDataSource>
This time, pressing F5 will show the Step1.aspx page.
To the Web Site, add a Page called Step2.aspx and set it up in much the same way as shown for Step1.aspx, this time with a Name TextBox
and an Over 18 CheckBox
indicator. The code for Step2.aspx is shown below:
<asp:ListView ID="ListView1" runat="server"
DataSourceID="CrumbTrailDataSource1" ItemPlaceholderID="PlaceHolder1">
<LayoutTemplate>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</LayoutTemplate>
<ItemTemplate>
<asp:HyperLink ID="HyperLink1" runat="server"
Text='<%# Eval("Title") %>' NavigateUrl='<%# Eval("NavigationLink") %>'>
</asp:HyperLink>
</ItemTemplate>
</asp:ListView>
<cc1:CrumbTrailDataSource ID="CrumbTrailDataSource1" runat="server">
</cc1:CrumbTrailDataSource>
<asp:FormView ID="FormView1" runat="server" DataSourceID="NavigationDataSource1"
DefaultMode="Edit">
<EditItemTemplate>
<div><asp:Label ID="Label1" runat="server"
Text="Name" AssociatedControlID="TextBox1" />
<asp:TextBox ID="TextBox1" runat="server"
Text="<%# Bind('[name]') %>" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
runat="server" ControlToValidate="TextBox1"
EnableClientScript="False"
ErrorMessage="Please enter a Name" /></div>
<div><asp:CheckBox ID="CheckBox1" runat="server"
Text="Over 18" Checked="<%# Bind('[over18]') %>" /></div>
<asp:Button ID="Button1" runat="server" Text="Next"
CommandName="Update" />
</EditItemTemplate>
</asp:FormView>
<cc1:NavigationDataSource ID="NavigationDataSource1" runat="server">
<SelectParameters>
<cc1:NavigationDataParameter Name="over18"
DefaultValue="false" DbType="Boolean"/>
</SelectParameters>
</cc1:NavigationDataSource>
The two main differences between Step2.aspx and Step1.aspx are:
- The
StateContext
Data item with key 'over18
' is bound to theChecked
property of theCheck
. ABox
null
value is not valid, as it is a boolean, so theSelectParameters
are used to initialize this to a bool offalse
. - A
ListView
control is bound to aCrumbTrailDataSource
to display the link back toStep1
fromStep2
(see the Data Binding section of http://navigation.codeplex.com/documentation).
To the Web Site, add a Page called Summary.aspx. To this, add the ListView
control bound to a CrumbTrailDataSource
as included in Step2.aspx so that the user can return to Step1
or Step2
when on this Summary
Page. Also add a FormView
databound to a NavigationDataSource
, as was done for Step1.aspx and Step2.aspx, but this time the Label
Text properties are set using the 1-way databinding syntax as they are read-only (similarly there is no Update Button). The code for Summary.aspx is shown below:
<asp:ListView ID="ListView1" runat="server"
DataSourceID="CrumbTrailDataSource1" ItemPlaceholderID="PlaceHolder1">
<LayoutTemplate>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</LayoutTemplate>
<ItemTemplate>
<asp:HyperLink ID="HyperLink1" runat="server" Text='<%# Eval("Title") %>'
NavigateUrl='<%# Eval("NavigationLink") %>'></asp:HyperLink>
</ItemTemplate>
</asp:ListView>
<cc1:CrumbTrailDataSource ID="CrumbTrailDataSource1" runat="server">
</cc1:CrumbTrailDataSource>
<asp:FormView ID="FormView1" runat="server" DataSourceID="NavigationDataSource1">
<ItemTemplate>
<div>Title: <asp:Label ID="Label1" runat="server"
Text='<%# Eval("[title]") %>' /></div>
<div>Gender: <asp:Label ID="Label2" runat="server"
Text='<%# Eval("[gender]") %>' /></div>
<div>Name: <asp:Label ID="Label3" runat="server"
Text='<%# Eval("[name]") %>' /></div>
<div>Over 18: <asp:Label ID="Label4" runat="server"
Text='<%# Eval("[over18]") %>' /></div>
</ItemTemplate>
</asp:FormView>
<cc1:NavigationDataSource ID="NavigationDataSource1" runat="server">
</cc1:NavigationDataSource>
All that remains is to Navigate between the States/steps. To do this, add ItemUpdated
listeners to the FormView
controls in Step1.aspx and Step2.aspx with the code shown below:
protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
StateController.Navigate("Next");
}
Pressing F5 and filling out the details required at each step and pressing Next should Navigate through to the Summary.aspx Page, although none of the information entered is displayed! However, the crumb trail links at the top of Step2.aspx and Summary.aspx work perfectly as they Navigate to the relevant Page with the user entered data retained, i.e., the controls are prepopulated with the user's selections.
To enable the Summary.aspx Page to display the information entered requires passing the user entered data to the Summary State (see the Navigation Data section of http://navigation.codeplex.com/documentation). So the Step1
State has to pass its values to Step2
and then Step2
has to pass its values together with Step1
's values to the Summary
State. Cleary the more States/steps there are, the more complicated this becomes as each one has to keep track of all the data passed into it and then pass this on together with the newly entered data. However, this is where the overloaded constructor of NavigationData
comes in useful as passing true
to this constructor will create a NavigationData
object containing all the current StateContext
Data. Hence change the Navigation in the ItemUpdated
listeners as shown below:
protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
StateController.Navigate("Next", new NavigationData(true));
}
This time pressing F5 and progressing through the steps will display the Summary.aspx Page showing all the user selections.
History
- 10th May, 2010: Initial post