Dynamically Adding Controls to a Page
ASP.NET supports dynamic control creation, and as a result, dynamic Web applications. You can use Repeater, DataList, or the DataGrid controls, or render dynamic HTML for a considerable degree of dynamic customization.
When you need complete customization, you can dynamically add programmatically created controls to a page. The controls must be added to a container control. You can use the PlaceHolder or Panel controls as containers or access the HtmlForm control through the page's Controls collection. The rest of the effort is just VB code.
Programmatically Adding Static Text to a Page
If you want to add static text dynamically to a page, you can create an instance of the Label control and place it on a container residing on a page (see Listing 19.7). To add the Label control, follow these steps:
Listing 19.7 Dynamically adding a Label control to a Web Form
1: Private Function GetForm() As HtmlForm 2: Return CType(FindControl("Form1"), HtmlForm) 3: End Function 4: 5: Private Sub Button1_Click(ByVal sender As System.Object, _ 6: ByVal e As System.EventArgs) Handles Button1.Click 7: 8: Dim Label As New Label() 9: Label.Text = "Label" 10: GetForm().Controls.Add(Label) 11: 12: End Sub
Line 8 creates an instance of the Label control. Line 9 assigns some arbitrary text to the Label.Text property. The GetForm query method returns the HtmlForm object, which plays the role of container in this example. Line 10 uses the HtmlForm.Controls collection to add the Label to the page.
Modifying Control Properties Using the Attributes Collection
In the preceding section, we modified the Label.Text property directly. You can also modify properties using the control's Attributes collection. For example, you might want to set the Name attribute of the Label; however, there is no matching Name property. The only way to modify this property programmatically is to use the attributes collection.
By adding the revision to the code from Listing 19.7, Listing 19.8 demonstrates how to modify attributes directly.
Listing 19.8 Modifying attributes directly
1: Private Sub Button1_Click(ByVal sender As System.Object, _ 2: ByVal e As System.EventArgs) Handles Button1.Click 3: 4: Dim Label As New Label() 5: Label.Text = "Label" 6: Label.Attributes.Add("Id", Label.Text) 7: Label.Attributes.Add("Name", Label.Text) 8: GetForm().Controls.Add(Label) 9: 10: End Sub
Lines 6 and 7 demonstrate code that sets the Id and Name attributes of the Label control. (Alternately, you can modify the Id attribute directly using the Label.Id property as follows : Label.Id = " text ".)
The Label control is rendered as a <span> tag. The HTML for the code in Listing 19.8 follows:
<span Id="Label" Name="Label">Label</span>
Use the properties when they are available, but remember that you can modify the attributes for the rendered HTML by name.
Programmatically Adding a LiteralControl to a Page
LiteralControl objects are those tags and elements that don't require processing on the server, like the line-break <br> tag. Notably, HTML elements that don't use or need the runat ="server" attribute can be implemented as LiteralControl instances.
To assist in positioning controls like the Label from the last section, we can use LiteralControlsHTMLto programmatically position the Label. Listing 19.9 demonstrates the revision to Listing 19.8 that adds a paragraph before the Label and a line-break after it.
Listing 19.9 Using LiteralControl objects
1: Private Sub Button1_Click(ByVal sender As System.Object, _ 2: ByVal e As System.EventArgs) Handles Button1.Click 3: 4: Dim Label As New Label() 5: Label.Text = "Label" & (GetForm().Controls.Count).ToString() 6: Label.Text = "Label" 7: Label.Attributes.Add("Id", Label.Text) 8: Label.Attributes.Add("Name", Label.Text) 9: GetForm().Controls.Add(New LiteralControl("<p>")) 10: GetForm().Controls.Add(Label) 11: GetForm().Controls.Add(New LiteralControl("<br>")) 12: 13: End Sub
Notice that the LiteralControl objects are created as parameters to the Controls.Add method. This is done for convenience. You're welcome to declare a variable and assign the instance to the variable before passing the literal control to the Add method.
Lines 9 and 11 define two instances of LiteralControl objects. The objects are represented by the literal HTML tags passed to the constructors.
Adding a Control Using the PlaceHolder
The previous listings demonstrated how to walk the page's Controls collection to find the HtmlForm and use that form as the container for dynamic controls. An easier operation is to add a PlaceHolder or Panel control to the page and place dynamic controls on the PlaceHolder control.
The technique for adding controls to a PlaceHolder is the same as adding dynamic controls to any other container:
You can add and arrange controls on the PlaceHolder just as you would on an HtmlForm or Panel. The next section demonstrates creating a dynamic, data-bound control using the PlaceHolder control as the container, including a code listing.
Programmatically Adding a Databound Control to a Page
Programmatically adding a databound control combines two of the techniques we have discussed in this chapter: dynamically creating a control and binding a control to a data source. To review, the numbered list that follows summarizes all of the steps to create a DropDownList control and bind it to the code database. This time we will use an OleDbConnection and an OleDbCommand instead of the OleDbDataAdapter, as shown in Listing 19.10.
Listing 19.10 An example of code that creates a control and binds it to data, programmatically
1: Private Sub CreateAndBind() 2: 3: Dim Connection As New _ 4: OleDbConnection(ConnectionString()) 5: Dim Command As New _ 6: OleDbCommand("SELECT ID, Name FROM SOURCE", Connection) 7: 8: Connection.Open() 9: Dim Reader As OleDbDataReader = _ 10: Command.ExecuteReader(CommandBehavior.Default) 11: 12: Dim List As New DropDownList() 13: List.DataTextField = "Name" 14: List.DataSource = Reader 15: 16: Response.Write(CType(Controls(1), HtmlForm).Name) 17: 18: PlaceHolder1.Controls.Add(List) 19: List.DataBind() 20: End Sub
Lines 1 through 20 demonstrate a more verbose variation of creating a connection and command instead of an adapter. Because we are only browsing the data in the DropDownList, we can use the OleDbDataReader, which provides unidirectional reading from the database. Lines 13 and 14 bind the DataTextField to a column in our result set and assigns the OleDbDataReader to the DataSource. When the code runs, it creates the DropDownList shown (expanded) in Figure 19.5.
Figure 19.5. A programmatically created and bound DropDownList.
Wiring Dynamic Controls to an Event Handler
Controls that don't respond to user feedback are not any more useful in Web Forms than they are in Window Forms. Now that you know how to programmatically add controls to a Web Form, you need to know how to wire them up.
Would you be surprised if I said this process was identical to wiring controls up in Windows Forms? That's right. All you need is a procedure, a delegate, and the AddHandler method. I can't say this enough: This kind of consistency is the hallmark of a "first-class" language and tools. A class is a class is a class (see Listing 19.11 for an example that adds an event handler). The fact that Microsoft has extended this consistency to ASP.NET via Visual Basic .NET means that when you learn how to work with objects in VB .NET, you know how to work with them in ASP.NET.
Listing 19.11 Hooking an event handler to the dynamic DropDownList
1: Private Sub SelectedIndexChanged(ByVal Sender As Object, _ 2: ByVal e As System.EventArgs) 3: 4: Dim Connection As New OleDbConnection(ConnectionString) 5: 6: Dim Command As New _ 7: OleDbCommand(BuildQuery(_ 8: CType(Sender, DropDownList).SelectedItem.Text), _ 9: Connection) 10: 11: Connection.Open() 12: 13: Dim Reader As OleDbDataReader = _ 14: Command.ExecuteReader() 15: 16: If (Reader.Read()) Then 17: TextBox1.Text = Reader.GetString(0) 18: End If 19: 20: End Sub 21: 22: 23: Private Sub CreateAndBind() 24: 25: Dim Connection As New _ 26: OleDbConnection(ConnectionString()) 27: Dim Command As New _ 28: OleDbCommand("SELECT ID, Name FROM SOURCE", Connection) 29: 30: Connection.Open() 31: Dim Reader As OleDbDataReader = _ 32: Command.ExecuteReader(CommandBehavior.Default) 33: 34: Dim List As New DropDownList() 35: List.DataTextField = "Name" 36: List.DataSource = Reader 37: 38: PlaceHolder1.Controls.Add(List) 39: 40: AddHandler List.SelectedIndexChanged, _ 41: AddressOf SelectedIndexChanged 42: 43: List.DataBind() 44: 45: End Sub
Lines 23 through 45 borrow from CreateAndBind in Listing 19.10. The additional code is the association of the event handler to the dynamic list box on lines 40 and 41. If you change the selected element in the list, the event handler SelectedIndexChanged will be invoked when the form is posted. Lines 1 through 20 implement the event handler.
SelectedIndexChanged creates an OleDbConnection, OleDbCommand, OleDbDataReader, and uses the value returned by the query to display the code in a textbox control. All of this code works dynamically because the handler is associated with the DropDownList.SelectedIndexChanged event on lines 40 and 41.