After a few days of searching forums, reading docs, and meeting with teammates I have yet to find a solution to this issue.
I have a form in a VB.NET project that dynamically adds user controls with the postback from a click of an asp button. The reason there is a postback is because there are calculations happening between the user control and the main page.
There is a dropdown list that needed to have the <optgroup>
tag. To do this, I wrote a list of data into an XML file with a structure that follows:
<AccountListData>
<account>
<group>OPTION GROUP NAME HERE</group>
<value>OPTION VALUE/TEXT HERE</value>
</account>
<account>...</account>
</AccountListData>
The XML file is 1117 lines long. It wasn't fun to write out. But, the reason I did was to use it to create the <select>
tag in the code behind and assign it to the innerHTML
attribute of a div
tag in the front end. It looks something like this:
Form.aspx
<table>
<tr>
<td id="DateLabel" runat="server">Date:</td>
<td id="DescriptionLabel" runat="server">Description:</td>
<td id="AmountLabel" runat="server">Amount:</td>
<td id="DropDownLabel" runat="server">Select:</td>
</tr>
<asp:Placeholder ID="ph1" runat="server"></asp:Placeholder>
</table>
<asp:Button ID="AddItemButton" runat="server" CausesValidation="false" Text="Add Item">
<asp:Label ID="RowCountLabel" runat="server"></asp:Label>
Form.aspx.vb
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
'Add/Removed User Control
AddRemoveUserControl()
ScriptManager.RegisterStartupScript(Me, Me.[GetType](), "CalculateCosts", "Javascript function();"), True)
End Sub
...
Public Function GetPostbackControlName(ByVal Page As Page) As String
Dim ctrlName As String
ctrlName = Page.Request.Params.Get("__EVENTTARGET")
Return ctrlName
End Function
...
Private Sub AddRemoveUserControl()
'Get and check the ID of what caused the postback
Dim c As Control = GetPostbackControlName(Page)
If Not IsNothing(c) Then
If c.ID.ToString = "AddItemButton" Then
RowCountLabel.Text = CInt(RowCountLabel.Text) + 1
End If
End If
'Get ID for dynamic user controls
Dim ControlID As Integer = 0
For i As Integer = 1 To (CInt(RowCountLabel) - 1)
Dim DynamicControl As ItemTable = LoadControl("usercontrols/ItemTable.ascx")
DynamicUserControl.ID = "uc" & CStr(ControlID)
'Add an event handler for the usercontrol
AddHandler DynamicUserControl.RemoveUserControl, AddressOf Me.HandleRemoveUserControl
'Add user control to placeholder on front end
ph1.Controls.Add(DynamicUserControl)
'Number the amount of user controls
ControlID += 1
Next
NumberItemTable()
End Sub
...
Private Sub HandleRemoveUserControl(sender As Object, e As EventArgs)
'Removes usercontrol from placeholder and lowers count #
End Sub
...
Private Sub NumberItemTable()
'Gives a number value to a property in the usercontrol
End Sub
Control.ascx
<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="ItemTable.ascx.vb" Inherits=".ItemTable" %>
<tr>
<td>
<asp:Textbox ID="Date" runat="server" CssClass="datepicker"></asp:Textbox></td>
<td>
<asp:Textbox ID="Description" runat="server"></asp:Textbox></td>
<td>
<asp:Textbox ID="Amount" runat="server"></asp:Textbox></td>
<td>
<div ID="DynamicSelect" runat="server"></div></td>
</tr>
Control.ascx.vb
Dim XMLInfo As XDocument
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
FillDropDownList()
End Sub
Private Sub FillDrowDownList()
'Get XML data
Dim XMLData 'all the XML data is loaded as an XDocument here
Dim temp As string
'WHERE THE DROPDOWN IS DYNAMICALLY CREATED WITH XML DATA
DynamicSelect.InnerHTML += "<select ID='" + Me.ID.ToString + "CreatedSelect'><option value='' selected disabled>- Select -</option>"
For Each element As XElement In XMLData.Descendants("account")
Dim group As String = element.<group>.Value.ToString
Dim value As String = element.<value>.Value.ToString
If group <> "" Then
If group <> temp Then
temp = group
DynamicSelect.InnerHTML += "</optgroup>"
DynamicSelect.InnerHTML += "<optgroup label='" + group + "'>"
End If
End If
DynamicSelect.InnerHTML += "<option value='" + value + "'>" + value + "</option>"
Next
DynamicSelect.InnerHTML += "</select>"
End Sub
When a value is selected on the dynamic dropdown list, any postback clears that selection. Any insight into why that is happening and what I can do to solve this issue would be GREATLY appreciated.
With two days of no answers and a total of 27 views, I doubt I'll get an answer to this question any time soon. So, I'll share with any of you that are looking for a solution to this issue that I came up with last night.
I realized that option groups are nothing but a list with a header, right? What is a header but some text that is just for display, and what is some text that is just for display when it comes to input? Disabled!
With this, I decided I was going to alter my code a bit and display a disabled field for each header using the asp DropDownList rather than try to create a dynamic select in the code behind, foregoing the data loss issue and securing the status of the viewstate.
My data structure stayed the same.
data.xml
<account>
<group>Group 1</group>
<value>Value 1</value>
</account>
My form methods stayed the same, and I'll just leave those above and not copy them down here again.
Here is where the change lies.
In my control, the front end changed very simply; replace the <div>
with the <asp:DropDownList>
.
Control.ascx
<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="ItemTable.ascx.vb" Inherits=".ItemTable" %>
<tr>
<td>
<asp:Textbox ID="Date" runat="server" CssClass="datepicker"></asp:Textbox></td>
<td>
<asp:Textbox ID="Description" runat="server"></asp:Textbox></td>
<td>
<asp:Textbox ID="Amount" runat="server"></asp:Textbox></td>
<td>
<div ID="DynamicSelect" runat="server"></div></td>
</tr>
My control code behind was changed to:
Control.ascx.vb
Imports System.Web.UI.WebControls
...
Private Sub FillDrowDownList()
'Get XML data
Dim XMLData 'all the XML data is loaded as an XDocument here
Dim temp As string = ""
Dim i As Integer = 0
'Check if the control already has the dropdown
If DynamicSelect.Items.Count < 1 Then
DynamicSelect.Items.Add(New ListItem("- Select -", ""))
DynamicSelect.Items(i).Attributes.Add("selected", "true")
DynamicSelect.Items(i).Attributes.Add("disabled", "true")
i += 1
'loop through XML data
For Each element As XElement In XMLData.Descendants("account")
Dim group As String = element.<group>.Value.ToString
Dim value As String = element.<value>.Value.ToString
If temp <> group Then
temp = group
DynamicSelect.Items.Add(New ListItem(group, ""))
DynamicSelect.Items(i).Attributes.Add("disabled", "true")
i += 1
End If
DynamicSelect.Items.Add(New ListItem(value, value))
i += 1
End If
End Sub
This new code produces a DropDownList with group headers! Rather than having the bold and italic headers, it just has a disabled header. Which you can always seek out with CSS selectors and style to your desire!
I hope anyone who is still looking for a super simple solution to this issue will come across this and find it helpful.