Home All Groups Group Topic Archive Search About

Retrieving Values from Dynamically Created Controls

Author
19 Dec 2006 6:17 AM
JDRaven
I've read a lot of posts on this issue, and was pretty sure all I needed to
do was cycle through the controls collection of the container, but I can't
seem to figure out where the controls are contained when I am adding them to
table cells (of table rows of an ASP.NET TABLE control).

Basically, I have a web form that contains some fields, and then a
dropdownlist where the user says how many rows of input they need (up to a
max of 15).  On the dropdownlist's _SelectedIndexChanged event, I make the
table visible and only add as many rows as I need.  Each row contains 6
columns, and each column contains 1 control - either a textbox or a
dropdownlist.

The code below works perfectly as far as displaying the input table with
desired controls, and all the controls seem to work as expected.  The problem
is that I can not seem to be able to retrieve the values later (when the
submit button is pushed).  The table's control collection is empty, and the
table's rows collection always seems empty (and rows count is zero) when
debugging at run time even though I can see the table populated with the
correct number of rows and I can populate the textboxes and dropdownlists as
expected in the browser before I submit.

I was expecting the programmatically defined controls to be members of the
controls collection for the table itself, the row's controls collection,
and/or the row's cells' collection, but as mentioned above the rows count
always appears as zero when I run this, and can not find the visible controls
contained anywhere.

The initial table is simply defined on the form as:

<p>
   <asp:Table ID="tblResources" runat="server" BorderColor="Black"
BorderStyle="Solid" BorderWidth="1px" GridLines="Both" Visible="False"
style="line-height: normal">
   </asp:Table>
</p>

It is initially hidden, and then when the user selects the number of rows to
display, it made visible and populated with rows, cells and controls using
the following code:

Protected Sub ddlNbrResources_SelectedIndexChanged(ByVal sender As Object,
ByVal e As System.EventArgs) Handles ddlNbrResources.SelectedIndexChanged

        Dim rowCnt As Integer
        Dim rowCtr As Integer
        Dim cellCnt As Integer
        Dim cellCtr As Integer

        tblResources.Visible = True

        rowCnt = CInt(ddlNbrResources.SelectedValue)  'User specified # of
rows
        cellCnt = 6       'Fixed # of table columns

        For rowCtr = 1 To rowCnt
            Dim tRow As New TableRow()
            For cellCtr = 1 To cellCnt
                Dim tCell As New TableCell()
                Select Case cellCtr
                    Case 1
                            Dim tbResName As TextBox = New TextBox()
                            tbResName.ID = "tbResName" & CStr(rowCtr)
                            tbResName.Width = 140
                            tCell.Controls.Add(tbResName)
                    Case 2
                            Dim tbResID As TextBox = New TextBox()
                            tbResID.ID = "tbResID" & CStr(rowCtr)
                            tbResID.Width = 50
                            tbResID.MaxLength = 6
                            tCell.Controls.Add(tbResID)
                    Case 3
                            Dim ddlResCountry As DropDownList = New
DropDownList()
                            ddlResCountry.ID = "ddlResCountry" & CStr(rowCtr)
                            ddlResCountry.Items.Add("")
                            ddlResCountry.Items.Add("Asia / Pacific")
                            ddlResCountry.Items.Add("Canada")
                            ddlResCountry.Items.Add("EMEA")
                            ddlResCountry.Items.Add("Latin America")
                            ddlResCountry.Items.Add("United States")
                            tCell.Controls.Add(ddlResCountry)
                    Case 4
                            Dim tbResFrom As TextBox = New TextBox()
                            tbResFrom.ID = "tbResFrom" & CStr(rowCtr)
                            tbResFrom.Width = 65
                            tCell.Controls.Add(tbResFrom)
                    Case 5
                            Dim tbResTo As TextBox = New TextBox()
                            tbResTo.ID = "tbResTo" & CStr(rowCtr)
                            tbResTo.Width = 65
                            tCell.Controls.Add(tbResTo)
                    Case 6
                            Dim ddlResAppOrg As DropDownList = New
DropDownList()
                            ddlResAppOrg.ID = "ddlResAppOrg" & CStr(rowCtr)
                            ddlResAppOrg.Items.Add("")
                            ddlResAppOrg.Items.Add("Apps")
                            ddlResAppOrg.Items.Add("BPO")
                            ddlResAppOrg.Items.Add("ITO")
                            ddlResAppOrg.Items.Add("EA")
                            ddlResAppOrg.Items.Add("SA")
                            ddlResAppOrg.Items.Add("BD")
                            tCell.Controls.Add(ddlResAppOrg)
                        End If
                    Case Else
                        tCell.Text = "oops - should not be here"
                End Select
                ' Add new TableCell object to row.
                tRow.Cells.Add(tCell)
            Next
            ' Add new row to table.
            tblResources.Rows.Add(tRow)
        Next

    End Sub

All the above works great, but when my submit button's click event later
occurs, I can't seem to find a way to cycle through the appropriate controls
collection to get the user-specific values.  I try something like (just to
build a string with the control IDs so I know I am finding them - this does
not yet attempt to use the controls values yet):

                Dim i, j As Integer
                Dim sb As New StringBuilder

                'Cycle through each row used in the resource table
                Dim ctrl As Control
                For i = 0 To CInt(ddlNbrResources.SelectedValue) - 1
                    For j = 0 To 5
                        For Each ctrl In
tblResources.Rows(i).Cells(j).Controls
                            sb.Append(ctrl.ID.ToString)
                        Next
                    Next
                Next

But when I run the above, the tblResources.Rows.Count is zero, the
tblResources.Controls collection appears empty, and the
tblResources.Rows.Controls collection appears empty even though I can see
them all on the form just fine and interact with them as expected.

Where/how can I find the values of the controls programmatically defined as
above at run time???

Sorry for the long post - but this has been eating at me for 3 days now and
I wanted to give you as much detail as possible to help me find a solution (a
solution that does not just give up and predefines the entire table with all
25 possible rows).

As always, thanks for all your assistance - I've always gotten great
feedback from this group, and look forward to your help on this issue!

Author
19 Dec 2006 8:06 AM
Walter Wang [MSFT]
Hi,

Based on my understanding, you're trying to create dynamic count of table
rows which have predefined server controls; and you want to obtain the
values user have input in them. However, you're seeing that the controls
doesn't get recreated upon postback.

This is the expected behavior, remember ASP.NET is stateless, the control
hierarchy needs to be re-created upon every postback at server-side. For
controls added dynamically, you need a way to re-create them at server-side
again. For more information, please refer to following KB article:

#HOW TO: Dynamically Create Controls in ASP.NET with Visual Basic .NET
http://support.microsoft.com/kb/317515



However, for your specific requirement, this is not the best way to do this
by doing this yourself. You can use the Repeater control to create those
dynamic controls for you, and use it to read the values after that:

                <asp:Repeater ID="repeater1" runat="server">
                    <HeaderTemplate>
                        <table>
                    </HeaderTemplate>
                    <ItemTemplate>
                        <tr>
                            <td>
                                <asp:TextBox ID="tbResName"
runat="server"></asp:TextBox></td>
                            <td>
                                <asp:TextBox ID="tbResID"
runat="server"></asp:TextBox></td>
                            <td>
                                <asp:DropDownList ID="ddlResCountry"
runat="server">
                                    <asp:ListItem></asp:ListItem>
                                    <asp:ListItem>Asia /
Pacific</asp:ListItem>
                                    <asp:ListItem>Canada</asp:ListItem>
                                    <asp:ListItem>EMEA</asp:ListItem>
                                    <asp:ListItem>Latin
America</asp:ListItem>
                                    <asp:ListItem>United
States</asp:ListItem>
                                </asp:DropDownList>
                            </td>
                            <td><asp:TextBox ID="tbResFrom"
runat="server"></asp:TextBox></td>
                            <td><asp:TextBox ID="tbResTo"
runat="server"></asp:TextBox></td>
                            <td>
                                <asp:DropDownList ID="ddlResAppOrg"
runat="server">
                                    <asp:ListItem></asp:ListItem>
                                    <asp:ListItem>Apps</asp:ListItem>
                                    <asp:ListItem>BPO</asp:ListItem>
                                    <asp:ListItem>ITO</asp:ListItem>
                                    <asp:ListItem>EA</asp:ListItem>
                                    <asp:ListItem>SA</asp:ListItem>
                                    <asp:ListItem>BD</asp:ListItem>
                                </asp:DropDownList>
                            </td>
                        </tr>
                    </ItemTemplate>
                    <FooterTemplate>
                        </table>
                    </FooterTemplate>
                </asp:Repeater>



Normally a Repeater control will be bound to a data source and you can use
data binding expression in the template to show bound data. Since here
you're only want to show some static controls, just the row count needs to
repeated, we can use an dumb array as the data source, --just the size is
the value selected by user:

        rowCnt = CInt(ddlNbrResources.SelectedValue) 'User specified # of  
rows()

        Dim dummy(rowCnt - 1) As Integer
        repeater1.DataSource = dummy
        repeater1.DataBind()


And here's how you can retrieve the values input by the user:

    Protected Sub button1_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles button1.Click
        For i As Integer = 0 To repeater1.Items.Count - 1
            Dim tbResName As TextBox =
CType(repeater1.Items(i).FindControl("tbResName"), TextBox)
            ' You can use FindControl to find those controls inside a
RepeaterItem
        Next
    End Sub


Hope this helps.


Sincerely,
Walter Wang (waw***@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications. If you are using Outlook Express, please make sure you clear the
check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
promptly.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
Author
19 Dec 2006 8:23 AM
JDRaven
Walter - thank you very much for the quick response, and for giving me the
option of pursuing my original way via the link, and the better way using the
Repeater control.  After a cursory review of the KB article I definitely want
to pursue use of the repeater.

Again, thanks, and have a great day!

Show quoteHide quote
"Walter Wang [MSFT]" wrote:

> Hi,
>
> Based on my understanding, you're trying to create dynamic count of table
> rows which have predefined server controls; and you want to obtain the
> values user have input in them. However, you're seeing that the controls
> doesn't get recreated upon postback.
>
> This is the expected behavior, remember ASP.NET is stateless, the control
> hierarchy needs to be re-created upon every postback at server-side. For
> controls added dynamically, you need a way to re-create them at server-side
> again. For more information, please refer to following KB article:
>
> #HOW TO: Dynamically Create Controls in ASP.NET with Visual Basic .NET
> http://support.microsoft.com/kb/317515
>
>
>
> However, for your specific requirement, this is not the best way to do this
> by doing this yourself. You can use the Repeater control to create those
> dynamic controls for you, and use it to read the values after that:
>
>                 <asp:Repeater ID="repeater1" runat="server">
>                     <HeaderTemplate>
>                         <table>
>                     </HeaderTemplate>
>                     <ItemTemplate>
>                         <tr>
>                             <td>
>                                 <asp:TextBox ID="tbResName"
> runat="server"></asp:TextBox></td>
>                             <td>
>                                 <asp:TextBox ID="tbResID"
> runat="server"></asp:TextBox></td>
>                             <td>
>                                 <asp:DropDownList ID="ddlResCountry"
> runat="server">
>                                     <asp:ListItem></asp:ListItem>
>                                     <asp:ListItem>Asia /
> Pacific</asp:ListItem>
>                                     <asp:ListItem>Canada</asp:ListItem>
>                                     <asp:ListItem>EMEA</asp:ListItem>
>                                     <asp:ListItem>Latin
> America</asp:ListItem>
>                                     <asp:ListItem>United
> States</asp:ListItem>
>                                 </asp:DropDownList>
>                             </td>
>                             <td><asp:TextBox ID="tbResFrom"
> runat="server"></asp:TextBox></td>
>                             <td><asp:TextBox ID="tbResTo"
> runat="server"></asp:TextBox></td>
>                             <td>
>                                 <asp:DropDownList ID="ddlResAppOrg"
> runat="server">
>                                     <asp:ListItem></asp:ListItem>
>                                     <asp:ListItem>Apps</asp:ListItem>
>                                     <asp:ListItem>BPO</asp:ListItem>
>                                     <asp:ListItem>ITO</asp:ListItem>
>                                     <asp:ListItem>EA</asp:ListItem>
>                                     <asp:ListItem>SA</asp:ListItem>
>                                     <asp:ListItem>BD</asp:ListItem>
>                                 </asp:DropDownList>
>                             </td>
>                         </tr>
>                     </ItemTemplate>
>                     <FooterTemplate>
>                         </table>
>                     </FooterTemplate>
>                 </asp:Repeater>
>
>
>
> Normally a Repeater control will be bound to a data source and you can use
> data binding expression in the template to show bound data. Since here
> you're only want to show some static controls, just the row count needs to
> repeated, we can use an dumb array as the data source, --just the size is
> the value selected by user:
>
>         rowCnt = CInt(ddlNbrResources.SelectedValue) 'User specified # of  
>  rows()
>
>         Dim dummy(rowCnt - 1) As Integer
>         repeater1.DataSource = dummy
>         repeater1.DataBind()
>
>
> And here's how you can retrieve the values input by the user:
>
>     Protected Sub button1_Click(ByVal sender As Object, ByVal e As
> System.EventArgs) Handles button1.Click
>         For i As Integer = 0 To repeater1.Items.Count - 1
>             Dim tbResName As TextBox =
> CType(repeater1.Items(i).FindControl("tbResName"), TextBox)
>             ' You can use FindControl to find those controls inside a
> RepeaterItem
>         Next
>     End Sub
>
>
> Hope this helps.
>
>
> Sincerely,
> Walter Wang (waw***@online.microsoft.com, remove 'online.')
> Microsoft Online Community Support
>
> ==================================================
> Get notification to my posts through email? Please refer to
> http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
> ications. If you are using Outlook Express, please make sure you clear the
> check box "Tools/Options/Read: Get 300 headers at a time" to see your reply
> promptly.
>
> Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
> where an initial response from the community or a Microsoft Support
> Engineer within 1 business day is acceptable. Please note that each follow
> up response may take approximately 2 business days as the support
> professional working with you may need further investigation to reach the
> most efficient resolution. The offering is not appropriate for situations
> that require urgent, real-time or phone-based interactions or complex
> project analysis and dump analysis issues. Issues of this nature are best
> handled working with a dedicated Microsoft Support Engineer by contacting
> Microsoft Customer Support Services (CSS) at
> http://msdn.microsoft.com/subscriptions/support/default.aspx.
> ==================================================
>
> This posting is provided "AS IS" with no warranties, and confers no rights.
>
>