Introduction
I often find myself in a situation where I have a user input form that I want to reuse, but I don't know how many times I'll be reusing it. For example, let's say you want to let users on your site upload images, but you don't know how many images they'll want to upload. Giving the user an "Add" button can let them dynamically add as many upload boxes to the page as they want.
That's fine if you have a single control that you want to add dynamically, but what if you have several controls in a complex layout? In that case, the easiest way to handle this is by wrapping the controls you want to add into a single user control, and then dynamically adding the user control to your page. I'll show you how to do that, how to remove user controls, and also how to access the values of the controls within the user control.
Using the code
First, let's create a user control and populate it with several controls for our "template". The user control content looks like this:
<table>
<tr>
<td>Textbox Example:</td>
<td>
<asp:TextBox ID="tbx1" runat="server" />
</td>
</tr>
<tr>
<td>Dropdown Example:</td>
<td>
<asp:DropDownList ID="ddl1" runat="server">
<asp:ListItem Text="Dropdown 1" />
<asp:ListItem Text="Dropdown 2" />
<asp:ListItem Text="Dropdown 3" />
</asp:DropDownList>
</td>
</tr>
<tr>
<td>Checkbox Example:</td>
<td>
<asp:CheckBoxList ID="cbx1" runat="server">
<asp:ListItem Text="Checkbox 1" />
<asp:ListItem Text="Checkbox 2" />
<asp:ListItem Text="Checkbox 3" />
</asp:CheckBoxList>
</td>
</tr>
</table>
<asp:Button ID="btnRemove" runat="server" Text="Remove" />
<hr />
Now, add an event handler to the code-behind on the user control so we can tell when the Delete button was clicked:
Partial Class WebUserControl
Inherits System.Web.UI.UserControl
Public Event RemoveUserControl As EventHandler
Protected Friend Sub btnRemove_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles btnRemove.Click
RaiseEvent RemoveUserControl(sender, e)
End Sub
End Class
Now that we have the controls we want to display, we'll add the user control to another page dynamically.
There are three important things we have to do in the page load. First, we need to determine what control fired the postback. We need to make sure the "Add" button was clicked.
This needs to be done in the page load event, instead of in the onclick
event for the "Add" button, because of the way the ASP.NET page lifecycle works. If we try to add any controls in the onclick
event for the Add button (instead of the page load), those values will be blown away when the page posts back.
If the "Add" button was clicked, the next thing to do is increment the count of the user controls to add to the page, so we know how many controls to display. I store this value in a Literal
control across postbacks.
We also need to attach an event handler to take care of the 'Delete' button on the user control when it gets clicked. This will remove the control from the Panel
and decrement the number of controls on the page.
Finally, once all the setup is taken care of, we can dynamically load all the requested user controls into a PlaceHolder
control.
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
AddAndRemoveDynamicControls()
End Sub
Private Sub AddAndRemoveDynamicControls()
Dim c As Control = GetPostBackControl(Page)
If Not IsNothing(c) Then
If c.ID.ToString = "btnAdd" Then
ltlCount.Text = Convert.ToInt16(ltlCount.Text) + 1
End If
End If
ph1.Controls.Clear()
Dim ControlID As Integer = 0
For i As Integer = 0 To (Convert.ToInt16(ltlCount.Text) - 1)
Dim DynamicUserControl As WebUserControl = _
LoadControl("WebUserControl.ascx")
While InDeletedList("uc" & ControlID) = True
ControlID += 1
End While
DynamicUserControl.ID = "uc" & ControlID
AddHandler DynamicUserControl.RemoveUserControl, _
AddressOf Me.HandleRemoveUserControl
ph1.Controls.Add(DynamicUserControl)
ControlID += 1
Next
End Sub
Private Function InDeletedList(ByVal ControlID As String) As Boolean
Dim DeletedList() As String = ltlRemoved.Text.Split("|")
For i As Integer = 0 To DeletedList.GetLength(0) - 1
If ControlID.ToLower = DeletedList(i).ToLower Then
Return True
End If
Next
Return False
End Function
Sub HandleRemoveUserControl(ByVal sender As Object, ByVal e As EventArgs)
Dim DynamicUserControl As WebUserControl = sender.parent
ph1.Controls.Remove(sender.parent)
ltlRemoved.Text &= DynamicUserControl.ID & "|"
ltlCount.Text = Convert.ToInt16(ltlCount.Text) - 1
End Sub
Protected Sub btnAdd_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles btnAdd.Click
End Sub
There's one more issue here. How do we get at the values stored in the user controls? It's actually fairly straightforward. We loop through all the controls in the PlaceHolder
, then look at the name of the control to determine if this is one of the user controls we added to the PlaceHolder
.
If it is, we can use FindControl
to access the values stored in textboxes, dropdowns, checkboxes, or anything else we added to the user control. Here, I'm getting those values and then writing them out to the screen:
Protected Sub btnDisplayValues_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles btnDisplayValues.Click
ltlValues.Text = ""
For Each c As Control In ph1.Controls
If c.GetType.Name.ToLower = "webusercontrol_ascx" Then
Dim uc As UserControl = CType(c, UserControl)
Dim tbx1 As TextBox = uc.FindControl("tbx1")
Dim ddl1 As DropDownList = uc.FindControl("ddl1")
Dim cbx1 As CheckBoxList = uc.FindControl("cbx1")
Dim sb As New System.Text.StringBuilder
sb.Append("Textbox value: " & tbx1.Text & "<br />")
sb.Append("Dropdown value: " & ddl1.SelectedValue & "<br />")
sb.AppendLine("Checkbox values: ")
For Each li As ListItem In cbx1.Items
If li.Selected = True Then
sb.Append(li.Value & "<br />")
End If
Next
sb.Append("<hr />")
ltlValues.Text &= sb.ToString
End If
Next
End Sub
I hope this has given you another way to think about dynamically adding and removing controls. Enjoy!