4.3. Simulating Multipage Forms Problem

Problem

You want to create a form that appears, from the user’s perspective, to consist of multiple pages, while keeping all of your code in one .aspx file and the code-behind that accompanies it.

Solution

Create one ASP.NET page. Use a Wizard control with a WizardStep control containing the HTML for each of the “virtual” pages you wish to display. The output of a typical multipage form is shown in Figures 4-1, 4-2, 4-3, 4-4 through 4-5. Examples 4-10, 4-11 through 4-12 showthe .aspx and code-behind files for an application that implements this solution.

Multipage form output (Section 1.1)

Figure 4-1. Multipage form output (Section 1.1)

Discussion

In classic ASP, a series of questions or prompts, such as those on a survey or wizard, is typically implemented using multiple ASP pages with each submitting to the next in turn. Because ASP.NET 2.0 allows you to submit a form to another page, this same approach can still be used; however, ASP.NET 2.0 provides a simpler approach using the Wizard control.

Multipage form output (Section 1.2)

Figure 4-2. Multipage form output (Section 1.2)

Multipage form output (Section 1.2.3)

Figure 4-3. Multipage form output (Section 1.2.3)

Multipage form output (Section 1.2.4)

Figure 4-4. Multipage form output (Section 1.2.4)

Multipage form output (Section 1.3)

Figure 4-5. Multipage form output (Section 1.3)

The Wizard control provides the infrastructure you need to present a series of virtual pages to the user including the requisite navigation controls for moving forward and backward in the series. The virtual pages are defined using a WizardStep control for each of the individual pages you want to present to the user. By defining the step type for the WizardStep controls (Start, Step, Finish, Complete, and Auto), the Wizard control will display the appropriate navigation buttons. The navigation buttons displayed for each step type are shown below.

Table 4-1. 

WizardStep type

Navigation buttons displayed

Start

Next

Step

Previous, Next

Finish

Previous, Complete

Complete

None

Auto

Automatically generates the navigation buttons as required by the position of the WizardStep control in the collection of WizardSteps

The example solution we present here uses the Wizard control and a series of WizardStep controls to display a series of questions in a short survey.

Tip

Refer to Recipe 4.2 if you are determined to stick to the multiple form approach.

In our example, the .aspx file contains one Wizard control and five WizardStep controls. The first WizardStep contains the first question (“Do you currently use ASP.NET 1.x?”) along with a RadioButtonList control for the response. The second WizardStep control contains the second question (“How long have you been using ASP.NET 1.x?”) along with a DropDownList control for the response. The third and fourth WizardStep controls are the same as the first and second steps with questions related to ASP.NET 2.0. The fifth WizardStep control contains a message thanking the user for taking the survey.

The Page_Load event handler in the code-behind initializes two arrays containing the possible answers to the questions and binds the data to the RadioButtonList and DropDownList controls in steps 1 through 4, as shown in Examples 4-11 (VB) and 4-12 (C#). We used this approach to reuse the data and to set the stage for potentially populating the available responses from a database.

We have implemented event handlers in the code-behind for the wizard’s next and previous button click events to demonstrate the ability to skip questions as a function of the user’s responses. In the event handler for the next button click event (wzSurvey_NextButtonClick), we check to see if the current step is for one of the “use” questions. If the user responds by indicating she has not used ASP.NET, there is no point in asking how long she has used the product, so we skip the next step using the wizard’s MoveTo method.


	'skip steps as a function of the users answers
	Select Case e.CurrentStepIndex
		Case 0
			If ((rbStep1.SelectedIndex >= 0) AndAlso _
				(rbStep1.SelectedItem.Text.Equals("No"))) Then
			'user does not use ASP.NET 1.x so move to 2.0 question
			wzSurvey.MoveTo(step3)
		End If

	  Case 2
	    If ((rbStep1.SelectedIndex >= 0) AndAlso _
		    (rbStep3.SelectedItem.Text.Equals("No"))) Then
		  'user does not use ASP.NET 2.0 so move to complete step
		  wzSurvey.MoveTo(step5)
	    End If

	 Case Else
	   'nothing required
   End Select


   // skip steps as a function of the users answers
   switch (e.CurrentStepIndex)
   {
	 case 0:
	   if ((rbStep1.SelectedIndex >= 0) &&
	       (rbStep1.SelectedItem.Text.Equals("No")))
	   {
	     // user does not use ASP.NET 1.x so move to 2.0 question
		 wzSurvey.MoveTo(step3);
	   }
	   break;

    case 2:
	  if ((rbStep1.SelectedIndex >= 0) &&
	      (rbStep3.SelectedItem.Text.Equals("No")))
	  {
	    // user does not use ASP.NET 2.0 so move to complete step
		wzSurvey.MoveTo(step5);
	  }
	    break;
	 
	  default:
	    // nothing required
		break;

    }

In the wizard’s previous button click event handler (wzSurvey_PreviousButtonClick), we do the same checks, but this time for the user navigating in the reverse direction.

When the final step is reached, the survey should be saved in your data store. We do this in the event handler for the ActiveStepChanged event by checking to see if the ActiveStepIndex is equal to the last step in the series.


	'check to see if the complete step is the active step
	If (wzSurvey.ActiveStepIndex = wzSurvey.WizardSteps.Count - 1) Then
		'survey is complete so production application should store the data		
		'in an applicable data store
	End If

	// check to see if the complete step is the active step
	if (wzSurvey.ActiveStepIndex == wzSurvey.WizardSteps.Count - 1)
	{ 
	  // survey is complete so production application should store the data
	  // in an applicable data store
	}

Warning

The Wizard control has a Finish button click event that can be used to trigger your code to save the results; however, if your implementation of the Wizard control (like our example) allows the user to skip steps, it is possible the Finish step will never be displayed and the Finish button click event will not occur.

In addition to sequential navigation, the Wizard control provides the ability to perform direct navigation through the series of steps by displaying a sidebar with links to each step, as shown in Figure 4-6. The sidebar is enabled by setting the DisplaySideBar attribute of the Wizard control to true.

	<asp:Wizard ID="wzSurvey" runat="server"
				   CssClass="wizardBody" Width="75%" align="center"
				   HeaderText="ASP.NET Usage Survey"
				   HeaderStyle-CssClass="wizardHeader"
				   StepStyle-HorizontalAlign="Left"
				   StepStyle-VerticalAlign="Middle"
				   DisplaySideBar="true"
				   SideBarStyle-CssClass="wizardSideBar"
				   OnNextButtonClick="wzSurvey_NextButtonClick"
				   OnPreviousButtonClick="wzSurvey_PreviousButtonClick"
				   OnActiveStepChanged="wzSurvey_ActiveStepChanged" >
Wizard control with sidebar navigation

Figure 4-6. Wizard control with sidebar navigation

By using the style attributes for each of the sections of the Wizard control, almost everything about the Wizard control can be configured to match the look and feel of your application.

See Also

Recipe 4.2

Example 4-10. Simulating a multipage form (.aspx)

<%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master"
	AutoEventWireup="false"
	CodeFile="CH04SurveyDataVB1.aspx.vb"
	Inherits="ASPNetCookbook.VBExamples.CH04SurveyDataVB1"
	Title="Using the Wizard Control For Data Collection" %>
<asp:Content ID="pageBody" Runat="server" ContentPlaceHolderID="PageBody">
	 <div align="center" class="pageHeading">
		Using the Wizard Control For Data Collection (VB)
	 </div>
	 <asp:Wizard ID="wzSurvey" runat="server"
								 CssClass="wizardBody" Width="75%" align="center"
								 HeaderText="ASP.NET Usage Survey"
								 HeaderStyle-CssClass="wizardHeader"
								 StepStyle-HorizontalAlign="Left"
								 StepStyle-VerticalAlign="Middle"
								 DisplaySideBar="false"
								 OnNextButtonClick="wzSurvey_NextButtonClick"
								 OnPreviousButtonClick="wzSurvey_PreviousButtonClick"
								 OnActiveStepChanged="wzSurvey_ActiveStepChanged" >
	<WizardSteps>
		<asp:WizardStep ID="step1" runat="server"
							StepType="Start" Title="1.x Use">
			<asp:Label ID="lbl1" runat="server"
							CssClass="wizardStep"
							Text="Do you currently use ASP.NET 1.x?" />
			<asp:RadioButtonList ID="rbStep1" Runat="server"
									RepeatLayout="Flow"
									RepeatDirection="Horizontal"
									CssClass="wizardStep" />
			</asp:WizardStep>

	<asp:WizardStep ID="step2" runat="server"
					   StepType="Step" Title="1.x Experience">
			<asp:Label ID="lbl2" runat="server"
					    CssClass="wizardStep"
						Text="How long have you been using ASP.NET 1.x?" />
			<asp:DropDownList ID="ddStep2" runat="server"
						    CssClass="wizardStep" />
	</asp:WizardStep>

	<asp:WizardStep ID="step3" runat="server"
					StepType="Step" Title="2.0 Use">
	<asp:Label ID="lbl3" runat="server"
				 CssClass="wizardStep"
				 Text="Do you currently use ASP.NET 2.0?" />
	<asp:RadioButtonList ID="rbStep3" Runat="server"
						    RepeatLayout="Flow"
							RepeatDirection="Horizontal"
							CssClass="wizardStep" />
	</asp:WizardStep>

	<asp:WizardStep ID="step4" runat="server"
					   StepType="Finish" Title="2.0 Experience">
		<asp:Label ID="lbl4" runat="server"
					  CssClass="wizardStep"
					  Text="How long have you been using ASP.NET 2.0?" />
		<asp:DropDownList ID="ddStep4" runat="server"
							 CssClass="wizardStep" />
	</asp:WizardStep>

	<asp:WizardStep ID="step5" runat="server"
						Ste1pType="Complete"
						Title="Complete" >
		<asp:Label ID="lbl5" runat="server"
						CssClass="wizardStep"
						Text="Thank you for taking our survey" />
		</asp:WizardStep>
	</WizardSteps>
  </asp:Wizard>
</asp:Content>

Example 4-11. Simulating a multipage form (.vb)

Option Explicit On
Option Strict On

Imports System.Web.UI.WebControls

Namespace ASPNetCookbook.VBExamples
	''' <summary>
	''' This class provides the code behind for
	''' CH04SurveyDataVB1.aspx 
	''' </summary>
	Partial Class CH04SurveyDataVB1
		Inherits System.Web.UI.Page

		'''********************************************************************
		''' <summary>
		''' This routine provides the event handler for the page load event. It
		''' is responsible for initializing the controls on the page.
		''' </summary>
		'''
		''' <param name="sender">Set to the sender of the event</param>
		''' <param name="e">Set to the event arguments</param>
		Protected Sub Page_Load(ByVal sender As Object, _
									   ByVal e As System.EventArgs) Handles Me.Load

		Dim yesNoSelections As ArrayList
		Dim experienceSelections As ArrayList

		If (Not Page.IsPostBack) Then
			'build the arraylist with the yes/no responses
			yesNoSelections = New ArrayList()
			yesNoSelections.Add(New ListItem("Yes", "1"))
			yesNoSelections.Add(New ListItem("No", "0"))
			
	    'bind the yes/no data to the radio button lists in the questions
		rbStep1.DataSource = yesNoSelections
		rbStep1.DataTextField = "Text"
		rbStep1.DataValueField = "Value"
		rbStep1.DataBind()	
		
		rbStep3.DataSource = yesNoSelections
		rbStep3.DataTextField = "Text"
		rbStep3.DataValueField = "Value"	
		rbStep3.DataBind()
		
		'build the arraylist with the experience responses
		experienceSelections = New ArrayList
		experienceSelections.Add(New ListItem("-- Select One --", "0"))
		experienceSelections.Add(New ListItem("0-6 Months", "1"))
		experienceSelections.Add(New ListItem("7-12 Months", "2"))
		experienceSelections.Add(New ListItem("13-24 Months", "3"))
		experienceSelections.Add(New ListItem("24+ Months", "4"))
		
		'bind the experience data to the radio button lists in the questions
		ddStep2.DataSource = experienceSelections
		ddStep2.DataTextField = "Text"
		ddStep2.DataValueField = "Value"
		ddStep2.DataBind()
		
		ddStep4.DataSource = experienceSelections
		ddStep4.DataTextField = "Text"
		ddStep4.DataValueField = "Value"
		ddStep4.DataBind()
	End If
End Sub 'Page_Load

'''*********************************************************************** ''' <summary>
''' This routine provides the event handler for the wizard's next button
''' click event. It is responsible for altering the survey navigation as
''' as function of the answers provided
''' </summary>
'''
''' <param name="sender"></param>
''' <param name="e"></param>
Protected Sub wzSurvey_NextButtonClick(ByVal sender As Object, _
								ByVal e As WizardNavigationEventArgs)
   'skip steps as a function of the users answers
   Select Case e.CurrentStepIndex
	 Case 0
	    If ((rbStep1.SelectedIndex >= 0) AndAlso _
		    (rbStep1.SelectedItem.Text.Equals("No"))) Then
		  'user does not use ASP.NET 1.x so move to 2.0 question
		 wzSurvey.MoveTo(step3)
	    End If

     Case 2
        If ((rbStep1.SelectedIndex >= 0) AndAlso _
	        (rbStep3.SelectedItem.Text.Equals("No"))) Then
		  'user does not use ASP.NET 2.0 so move to complete step
		 wzSurvey.MoveTo(step5)
	    End If

     Case Else
		'nothing required
    End Select
  End Sub 'wzSurvey_NextButtonClick

  '''**********************************************************************
  ''' <summary>
  ''' This routine provides the event handler for the wizard's prev button
  ''' click event. It is responsible for altering the survey navigation as
  ''' as function of the answers provided
  ''' </summary>
  '''
  ''' <param name="sender"></param>
  ''' <param name="e"></param>
  Protected Sub wzSurvey_PreviousButtonClick(ByVal sender As Object, _
									  ByVal e As WizardNavigationEventArgs)	
	'skip steps as a function of the users answers
	Select Case e.CurrentStepIndex
		Case 2
		  If ((rbStep1.SelectedIndex >= 0) AndAlso _
		      (rbStep3.SelectedItem.Text.Equals("No"))) Then
			'user does not use ASP.NET 2.0 so move to 1.x question
			wzSurvey.MoveTo(step1)
		  End If
		Case Else
		  'nothing required
	End Select
  End Sub 'wzSurvey_PreviousButtonClick
  
 '''***********************************************************************
 ''' <summary>
 ''' This routine provides the event handler for the wizard's active step
 ''' changed event. It is responsible for determining if the survey is
 ''' complete and storing the data in the data store.
 ''' </summary>
 '''
 ''' <param name="sender"></param>
 ''' <param name="e"></param>
 Protected Sub wzSurvey_ActiveStepChanged(ByVal sender As Object, _
										 ByVal e As System.EventArgs)
   'check to see if the complete step is the active step
   If (wzSurvey.ActiveStepIndex = wzSurvey.WizardSteps.Count - 1) Then
     'survey is complete so production application should store the data
	 'in an applicable data store
   End If
  End Sub  'wzSurvey_ActiveStepChanged
 End Class 'CH04SurveyDataVB1
End Namespace

Example 4-12. Simulating a multipage form (.cs)

using System;
using System.Collections;
using System.Web.UI.WebControls;

namespace ASPNetCookbook.CSExamples
{
	/// <summary>
	/// This class provides the code behind for
	/// CH04SurveyDataCS1.aspx
	/// </summary>
	public partial class CH04SurveyDataCS1 : System.Web.UI.Page
	{
	  ///******************************************************************
	  /// <summary>
	  /// This routine provides the event handler for the page load event.
	  /// It is responsible for initializing the controls on the page.
	  /// </summary>
	  ///
	  /// <param name="sender">Set to the sender of the event</param>
	  /// <param name="e">Set to the event arguments</param>
     
      protected void Page_Load(object sender, EventArgs e)
	  {
		ArrayList yesNoSelections;
		ArrayList experienceSelections;

		if (!Page.IsPostBack)
		{
			// build the arraylist with the yes/no responses		
			yesNoSelections = new ArrayList();
			yesNoSelections.Add(new ListItem("Yes", "1"));
			yesNoSelections.Add(new ListItem("No", "0"));

			// bind the yes/no data to the radio button lists in the questions
			rbStep1.DataSource = yesNoSelections;
			rbStep1.DataTextField = "Text";
			rbStep1.DataValueField = "Value";
			rbStep1.DataBind();

			rbStep3.DataSource = yesNoSelections;
			rbStep3.DataTextField = "Text";
			rbStep3.DataValueField = "Value";
			rbStep3.DataBind();

			// build the arraylist with the experience responses
			experienceSelections = new ArrayList();
			experienceSelections.Add(new ListItem("-- Select One --", "0"));
			experienceSelections.Add(new ListItem("0-6 Months", "1"));
			experienceSelections.Add(new ListItem("7-12 Months", "2"));
			experienceSelections.Add(new ListItem("13-24 Months", "3"));
			experienceSelections.Add(new ListItem("24+ Months", "4"));

			// bind the experience data to the radio button lists in the questions
			ddStep2.DataSource = experienceSelections;
			ddStep2.DataTextField = "Text";
			ddStep2.DataValueField = "Value";
			ddStep2.DataBind();

			ddStep4.DataSource = experienceSelections;
			ddStep4.DataTextField = "Text";
			ddStep4.DataValueField = "Value";
			ddStep4.DataBind();
		 }
	  } // Page_Load

     ///*******************************************************************
 	 /// <summary>
	 /// This routine provides the event handler for the wizard's next button
	 /// click event. It is responsible for altering the survey navigation as
	 /// as function of the answers provided
	 /// </summary>
	 ///
	 /// <param name="sender"></param>
	 /// <param name="e"></param>
	 protected void wzSurvey_NextButtonClick(Object sender,
										 WizardNavigationEventArgs e)
	 {
		// skip steps as a function of the users answers
		switch (e.CurrentStepIndex)
		{
			case 0:
			   if ((rbStep1.SelectedIndex >= 0) &&
			       (rbStep1.SelectedItem.Text.Equals("No")))
	    	   {
			      // user does not use ASP.NET 1.x so move to 2.0 question
				  wzSurvey.MoveTo(step3);
       			}
				break;

			case 2:
			  if ((rbStep1.SelectedIndex >= 0) &&
          		  (rbStep3.SelectedItem.Text.Equals("No")))
			   {
				 // user does not use ASP.NET 2.0 so move to complete step
				 wzSurvey.MoveTo(step5);
			    }
				break;
				
			default:
			   // nothing required
			   break;
		}
	} // wzSurvey_NextButtonClick
  
   ///*********************************************************************
   /// <summary>
   /// This routine provides the event handler for the wizard's prev button
   /// click event. It is responsible for altering the survey navigation as
   /// as function of the answers provided
   /// </summary>
   ///
   /// <param name="sender"></param>
   /// <param name="e"></param>
   protected void wzSurvey_PreviousButtonClick(Object sender,
											   WizardNavigationEventArgs e)   
   {
	  // skip steps as a function of the users answers
	  switch (e.CurrentStepIndex)
	  {
		 case 2:
		    if ((rbStep1.SelectedIndex >= 0) &&
			    (rbStep3.SelectedItem.Text.Equals("No")))
			{
			   // user does not use ASP.NET 2.0 so move to 1.x question
			   wzSurvey.MoveTo(step1);
			}
			break;
			
		default:
		  // nothing required
		  break;
	 }
   } // wzSurvey_PreviousButtonClick

  ///**********************************************************************
  /// <summary>
  /// This routine provides the event handler for the wizard's active step
  /// changed event. It is responsible for determining if the survey is
  /// complete and storing the data in the data store.
  /// </summary>
  ///
  /// <param name="sender"></param>
  /// <param name="e"></param>


  protected void wzSurvey_ActiveStepChanged(Object sender,
											System.EventArgs e)
  {
    // check to see if the complete step is the active step
	if (wzSurvey.ActiveStepIndex == wzSurvey.WizardSteps.Count - 1)
	{
      // survey is complete so production application should store the data
      // in an applicable data store
	}
  } // wzSurvey_ActiveStepChanged
 } // CH04SurveyDataCS1
}

Get ASP.NET 2.0 Cookbook, 2nd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.