Dynamically Loading User Controls


You can dynamically load a User control at runtime and display it in a page. Imagine, for example, that you want to display different featured products randomly on the home page of your website. However, you want to display each featured product with a completely different layout. In that case, you can create a separate User control for each product and load one of the User controls randomly at runtime.

You load a User control with the Page.LoadControl() method. This method returns an instance of the Control class that you can add to a page. Typically, you add the User control to a PlaceHolder control that you have declared on the page.

Note

The PlaceHolder control was designed to do absolutely nothing. It simply acts as a placeholder on the page where you can add other controls.


For example, the page in Listing 7.14 randomly loads one of the controls from the FeaturedProducts folder and adds the control to the page.

Listing 7.14. ShowFeaturedProduct.aspx

<%@ Page Language="VB" %> <%@ Import Namespace="System.IO" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     Const randomFolder As String = "~/FeaturedProducts"     Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)         Dim featuredProductPath As String = GetRandomProductPath()         Dim featuredProduct As Control = Page.LoadControl(featuredProductPath)         PlaceHolder1.Controls.Add(featuredProduct)     End Sub     Private Function GetRandomProductPath() As String         Dim rnd As New Random()         Dim files() As String = Directory.GetFiles(MapPath(randomFolder),"*.ascx")         Dim featuredProductPath As String =  Path.GetFileName(files(rnd.Next(files.Length)))         Return Path.Combine(randomFolder,featuredProductPath)     End Function </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show Featured Products</title> </head> <body>     <form  runat="server">     <div>     <asp:PlaceHolder                  Runat="server" />     </div>     </form> </body> </html>

Using the Reference Directive

When you load a User control with the Page.LoadControl() method, the User control is returned as an instance of the System.Web.UI.Control class. This means that if the User control includes any custom properties, the properties aren't available when you dynamically load the User control.

If you dynamically load a User control, then you need to cast the control to the correct type before you can access any of the control's custom properties. To get a reference to a User control's type, you must use the <%@ Reference %> directive.

For example, imagine that you need to create a form that displays different questions depending on the answers that a user provides for previous questions. In that case, you can dynamically load different User controls that contain the different sets of questions.

For example, the page in Listing 7.15 contains a survey form. The first question asks you whether you are currently using ASP Classic or ASP.NET. Depending on your answer, the remainder of the form displays different questions (see Figure 7.5).

Figure 7.5. Displaying a survey form with dynamically loaded questions.


Listing 7.15. WebSurvey.aspx

[View full width]

<%@ Page Language="VB" %> <%@ Reference Control="~/ASPSurvey.ascx" %> <%@ Reference Control="~/ASPNetSurvey.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     Private _survey As Control = Nothing     Private Sub Page_Load()         Select Case ddlLanguage.SelectedIndex             Case 1                 _survey = Page.LoadControl("ASPSurvey.ascx")             Case 2                 _survey = Page.LoadControl("ASPNetSurvey.ascx")         End Select         If Not IsNothing(_survey) Then             PlaceHolder1.Controls.Add(_survey)         End If     End Sub     Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs)         Select Case ddlLanguage.SelectedIndex             Case 1                 Dim aspResults As ASPSurvey = CType(_survey, ASPSurvey)                 ltlResults.Text = "<h1>ASP Survey</h1>"                 ltlResults.Text += "<br />Know slow? " + aspResults.KnowSlow.ToString()                 ltlResults.Text += "<br />Know outdated? " + aspResults.KnowOutdated .ToString()             Case 2                 Dim aspNetResults As ASPNetSurvey = CType(_survey, ASPNetSurvey)                 ltlResults.Text = "<h1>ASP.NET Survey</h1>"                 ltlResults.Text += "<br />Know fast? " + aspNetResults.KnowFast.ToString()                 ltlResults.Text += "<br />Know newest? " + aspNetResults.KnowNewest.ToString()         End Select     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         html         {             font:14px Arial,Sans-Serif;         }     </style>     <title>Web Survey</title> </head> <body>     <form  runat="server">     <div>     <asp:Label                  Text="What language do you use to develop Web applications?"         Runat="server" />     <br />     <asp:DropDownList                  ToolTip="Web application language (reloads form)"         AutoPostBack="true"         Runat="server">         <asp:ListItem Text="Select Language" />         <asp:ListItem Text="ASP Classic"  />         <asp:ListItem Text="ASP.NET" />     </asp:DropDownList>     <br /><br />     <asp:PlaceHolder                  Runat="server" />     <asp:Button                  Text="Submit"         OnClick="btnSubmit_Click"         Runat="server" />     <hr />     <asp:Literal                  Runat="server" />     </div>     </form> </body> </html>

Web Standards Note

The DropDownList control in Listing 7.15 reloads the page automatically when you select a new option. You should never reload a page without warning the user because this can be very confusing for someone who is using an assistive device such as a screen reader. In Listing 7.15, a warning is added to the ToolTip property of the DropDownList control.


Depending on the user's selection from the DropDownList control, one of two User controls is loaded in the Page_Load() event handler: the ASPSurvey.ascx or the ASPNetSurvey.ascx User control. These controls are contained in Listing 7.16 and Listing 7.17.

When you submit the survey form, the btnSubmit_Click() method executes. This method casts the User control loaded in the form to the correct type. It casts the User control to either the ASPSurvey or the ASPNetSurvey type.

Notice that the page in Listing 7.15 includes two <%@ Reference %> directives. These reference directives enable you to cast the User control to the correct type so that you can access custom properties of the control such as the KnowSlow and KnowOutdated properties.

Listing 7.16. ASPSurvey.ascx

<%@ Control Language="VB" ClassName="ASPSurvey" %> <script runat="server">     Public ReadOnly Property KnowSlow() As Boolean         Get             Return chkSlow.Checked         End Get     End Property     Public ReadOnly Property KnowOutdated() As Boolean         Get             Return chkOutdated.Checked         End Get     End Property </script> <asp:CheckBox          Text="Did you know that ASP Classic is slow?"     Runat="server" /> <br /><br /> <asp:CheckBox          Text="Did you know that ASP Classic is outdated?"     Runat="server" /> <br /><br />

Listing 7.17. ASPNetSurvey.ascx

<%@ Control Language="VB" ClassName="ASPNetSurvey" %> <script runat="server">     Public ReadOnly Property KnowFast() As Boolean         Get             Return chkFast.Checked         End Get     End Property     Public ReadOnly Property KnowNewest() As Boolean         Get             Return chkNewest.Checked         End Get     End Property </script> <asp:CheckBox          Text="Did you know that ASP.NET is fast?"     Runat="server" /> <br /><br /> <asp:CheckBox          Text="Did you know that ASP.NET is the newest Microsoft         technology for building Web applications?"     Runat="server" /> <br /><br />

Creating a Multi-Page Wizard

This final section discusses how you can create a multi-page wizard by dynamically loading different user controls into the same page. This is going to be a complicated sample, but it is a realistic sample of situations when you would want to load User controls dynamically (see Figure 7.6).

Figure 7.6. Displaying a wizard with a series of User controls.


Imagine that you must create a form with 200 questions in it. Displaying all 200 questions to a user in a single form would be overwhelming. Instead, it makes more sense to break the form into multiple pages. Each page of questions can be represented with a User control.

First, you need to define an interface, named the IWizardStep interface, which all the User controls will implement. An interface enables you to know, in advance, that a User control supports a particular set of properties or methods.

Note

You need to add the interface in Listing 7.18 to your application's App_Code folder. In Visual Web Developer, create the interface by selecting Website, Add New Item, and select Class. Visual Web Developer prompts you to create the App_Code folder.


The IWizardStep interface is contained in Listing 7.18.

Listing 7.18. IWizardStep.vb

Public Interface IWizardStep     Sub LoadStep()     Function NextStep() As Boolean End Interface

The interface in Listing 7.18 contains two methods: LoadStep() and NextStep(). The LoadStep() method is called when a User control is first loaded. The NextStep() method is called when the Next button is clicked in the wizard.

Notice that the NextStep() method returns a Boolean value. If the NextStep() method returns the value False, then the user doesn't advance to the next wizard step.

This wizard will consist of the three wizard steps contained in Listing 7.19, Listing 7.20, and Listing 7.21.

Listing 7.19. WizardSteps\Step1.ascx

<%@ Control Language="VB" ClassName="Step1" %> <%@ Implements Interface="IWizardStep" %> <script runat="server">     Public Sub LoadStep() Implements IWizardStep.LoadStep         If Not IsNothing(Session("FirstName")) Then             txtFirstName.Text = CStr(Session("FirstName"))         End If         If Not IsNothing(Session("LastName")) Then             txtLastName.Text = CStr(Session("LastName"))         End If     End Sub     Public Function NextStep() As Boolean Implements IWizardStep.NextStep         If Page.IsValid Then             Session("FirstName") = txtFirstName.Text             Session("LastName") = txtLastName.Text             Return True         End If         Return False     End Function </script> <h1>Step 1</h1> <asp:Label          Text="First Name:"     AssociatedControl     Runat="server" /> <asp:RequiredFieldValidator          Text="(required)"     ControlToValidate="txtFirstName"     Runat="server" /> <br /> <asp:TextBox          Runat="server" /> <br /><br /> <asp:Label          Text="Last Name:"     AssociatedControl     Runat="server" /> <asp:RequiredFieldValidator          Text="(required)"     ControlToValidate="txtLastName"     Runat="server" /> <br /> <asp:TextBox          Runat="server" />

The wizard step in Listing 7.19 contains a simple form that contains Textbox controls for the user's first and last name. Both TextBox controls are validated with RequiredFieldValidator controls.

Notice that the User control in Listing 7.19 implements the IWizardStep interface. It contains an <%@ Implements %> directive at the top of the control.

The LoadStep() method assigns values to the txtFirstName and txtLastName TextBox controls from Session state. The NextStep() method grabs the values from the txtFirstName and txtLastName TextBox controls and assigns the values to Session state.

The second step of the Wizard is contained in Listing 7.20.

Listing 7.20. WizardSteps\Step2.ascx

<%@ Control Language="VB" ClassName="Step2" %> <%@ Implements Interface="IWizardStep" %> <script runat="server">     Public Sub LoadStep() Implements IWizardStep.LoadStep         If Not IsNothing(Session("FavoriteColor")) Then             txtFavoriteColor.Text = CStr(Session("FavoriteColor"))         End If     End Sub     Public Function NextStep() As Boolean Implements IWizardStep.NextStep         If Page.IsValid Then             Session("FavoriteColor") = txtFavoriteColor.Text             Return True         End If         Return False     End Function </script> <h1>Step 2</h1> <asp:Label          Text="Favorite Color:"     AssociatedControl     Runat="server" /> <asp:RequiredFieldValidator          Text="(required)"     ControlToValidate="txtFavoriteColor"     Runat="server" /> <br /> <asp:TextBox          Runat="server" />

The User control in Listing 7.20 also implements the IWizardStep interface. In this step, the user enters a favorite color.

The final wizard step is contained in Listing 7.21.

Listing 7.21. WizardSteps\Step3.ascx

<%@ Control Language="VB" ClassName="Step3" %> <%@ Implements Interface="IWizardStep" %> <script runat="server">     Public Sub LoadStep() Implements IWizardStep.LoadStep         lblFirstName.Text = CStr(Session("FirstName"))         lblLastName.Text = CStr(Session("LastName"))         lblFavoriteColor.Text = CStr(Session("FavoriteColor"))     End Sub     Public Function NextStep() As Boolean Implements IWizardStep.NextStep         Return False     End Function </script> <h1>Step 3</h1> First Name: <asp:Label          Runat="server" /> <br /> Last Name: <asp:Label          Runat="server" /> <br /> Favorite Color: <asp:Label          Runat="server" />

The wizard step in Listing 7.21 displays a summary of answers that the user has provided in the first two wizard steps (see Figure 7.7). Notice that it also implements the IWizardStep interface. Because this is the final wizard step, the NextStep() method always returns the value False.

Figure 7.7. Displaying the wizard summary step.


The page in Listing 7.22 contains the actual wizard. This page loads each of the wizard steps.

Listing 7.22. Wizard.aspx

<%@ Page Language="VB" %> <%@ Import Namespace="System.Collections.Generic" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     Private _wizardSteps As New List(Of String)()     Private _currentStep As Control     ''' <summary>     ''' The current step in the Wizard     ''' </summary>     Public Property StepIndex() As Integer         Get             If (IsNothing(ViewState("StepIndex"))) Then                 Return 0             else                 Return CInt(ViewState("StepIndex"))             End If        End Get        Set (ByVal Value As Integer)             ViewState("StepIndex") = value        End Set     End Property     ''' <summary>     ''' Load the list of wizard steps and load     ''' current step     ''' </summary>     Sub Page_Load()         _wizardSteps.Add("~/WizardSteps/Step1.ascx")         _wizardSteps.Add("~/WizardSteps/Step2.ascx")         _wizardSteps.Add("~/WizardSteps/Step3.ascx")         LoadWizardStep()     End Sub     ''' <summary>     ''' Load the current wizard step     ''' </summary>     Private Sub LoadWizardStep()         _currentStep = Page.LoadControl(_wizardSteps(StepIndex))         _currentStep.ID = "ctlWizardStep"         plhWizardStep.Controls.Clear()         plhWizardStep.Controls.Add(_currentStep)         CType(_currentStep, IWizardStep).LoadStep()         ltlStep.Text = String.Format("Step {0} of {1}", StepIndex + 1, _wizardSteps.Count)     End Sub     ''' <summary>     ''' Disable the Previous and Next     ''' buttons when appropriate     ''' </summary>     Sub Page_PreRender()         btnPrevious.Enabled = StepIndex > 0         btnNext.Enabled = StepIndex < _wizardSteps.Count - 1     End Sub     ''' <summary>     ''' Execute the step's NextStep() method     ''' and move to the next step     ''' </summary>     Sub btnNext_Click(ByVal sender As Object, ByVal s As EventArgs)         Dim success As Boolean = CType(_currentStep, IWizardStep).NextStep()         If success Then             If (StepIndex < _wizardSteps.Count - 1) Then                 StepIndex = StepIndex + 1                 LoadWizardStep()             End If         End If     End Sub     ''' <summary>     ''' Move to the previous step     ''' </summary>     Sub btnPrevious_Click(ByVal sender As Object, ByVal e As EventArgs)         If StepIndex > 0 Then             StepIndex = StepIndex - 1             LoadWizardStep()         End If     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         html         {             font:14px Georgia,Serif;         }         fieldset         {             display:block;             width:600px;             padding:20px;             margin:10px;         }     </style>     <title>Wizard</title> </head> <body>     <form  runat="server">     <div>     <asp:Label                  Runat="server" />     <fieldset>     <legend><asp:Literal  runat="server" /></legend>         <asp:PlaceHolder                          Runat="server" />     </fieldset>     <asp:Button                  Text="&lt; Previous"         CausesValidation="false"         OnClick="btnPrevious_Click"         Runat="server" />     <asp:Button                  Text="Next &gt;"         OnClick="btnNext_Click"         Runat="server" />     </div>     </form> </body> </html>

The list of wizard steps is created in the Page_Load() method. The path to each wizard step User control is added to a collection of wizard steps.

The StepIndex property represents the index of the wizard step to display. Notice that the value of this property is stored in ViewState so that the value is available across multiple page requests.

The current wizard step is loaded by the LoadWizardStep() method. This method uses the StepIndex to grab the path to the current wizard step. Next, it uses the Page.LoadControl() method to actually load the wizard step User control.

After the LoadWizardStep() method loads the current wizard step, it calls the control's LoadStep() method and initializes the control.

The page also contains a Previous and Next button. When you click the Previous button, the btnPrevious_Click() method is called and the StepIndex is reduced by one. When you click the Next button, the btnNext_Click() method is called.

The btnNext_Click() method first calls the current wizard step's NextStep() method. If this method returns the value true, then one is added to the StepIndex property and the next wizard step is loaded. Otherwise, if the NextStep() method returns false, the next wizard step is not loaded.




ASP. NET 2.0 Unleashed
ASP.NET 2.0 Unleashed
ISBN: 0672328232
EAN: 2147483647
Year: 2006
Pages: 276

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net