0

I am using Visual Studio with asp.net, vb.net, and webforms.

My issue is that I need to get the user's textbox input from my dynamically generated textboxes.

On my button click BtnStep4Next, I dynamically add ServiceForm.ascx (it is a form with FirstName, LastName, City, etc). I add the Service Form to a list, and the list is stored in a session. There could be, for example, 5 webforms (ServiceForm).

Protected Sub BtnStep4Next_Click(sender As Object, e As EventArgs) Handles BtnStep4Next.Click

    Dim formCount As Integer = 0

    'Create a list to hold all of the service forms
    Dim listServiceForms As New List(Of openapps_luesreg_ServiceForm)
    Session("vsServiceForms") = listServiceForms

    For Each i In CBLModifications.Items
        'Do not show a service form for membership (ID = 1)
        If i.Value <> "1" Then
            formCount += 1

            Dim serviceForm As openapps_luesreg_ServiceForm = New openapps_luesreg_ServiceForm()

            'Load a new Service Form to the page
            serviceForm = Page.LoadControl("ServiceForm.ascx")
            serviceForm.ID = "service" + formCount.ToString

            Dim txtCity As TextBox = CType(serviceForm.FindControl("txtCity"), TextBox)
            txtCity.ID = "serviceCity" + formCount.ToString

            'Dim txtCityRegEx As Regex = CType(serviceForm.FindControl("RegExCity"), Regex)
            'txtCityRegEx.ID = "serviceCity" + formCount.ToString


            'Add the new Service Form to the panel PnlService Forms
            PnlServiceForms.Controls.Add(serviceForm)

            'Add the name to the top of the Form
            Dim lblServiceName As Label = CType(serviceForm.FindControl("lblServiceName"), Label)
            lblServiceName.Text = i.Text

            'Add to listServiceForms
            listServiceForms.Add(serviceForm)

            'Set the list to the ViewState
            Session("vsServiceForms") = listServiceForms

        End If
    Next
 End Sub

On a different button click, now I want to grab the user's input they type into the textboxes. My code here though results in empty strings (the original form text) instead of the user's updated input. How can I actually get each textboxes' text that the user enters at this point?

Protected Sub BtnStep5Next_Click(sender As Object, e As EventArgs) Handles BtnStep5Next.Click


    Dim listServiceForms As List(Of openapps_luesreg_ServiceForm)
    listServiceForms = DirectCast(Session("vsServiceForms"), List(Of openapps_luesreg_ServiceForm))

    Dim myCount As Integer = 0
    For Each i In listServiceForms
        myCount += 1
        Dim ctlTxtCity As String = "serviceCity" + myCount.ToString

        Dim myCity2 = ctlTxtCity

        Dim txtNewCity As TextBox =  CType(i.FindControl(ctlTxtCity), TextBox)
        Dim myCity = txtNewCity.Text

    Next
End Sub

1 Answer 1

1

You are writing way too much code. I mean, if I need 10 variables, do I go

 dim v1    as integer
 dim v2    as integer
 ... and so on

No! You do this:

dim MyValues(10) as integer

We use array's, collections. I mean even on desktop software. If I need a bunch of rows of repeating data - you use a grid, continues form or whatever other REPEATING type of control to do all this dirty work for you.

Say, I want to enter some Hotel Names. But, ahead of time I had no idea of how many. I mean, any database system + problem WILL have to deal with this type of scenario.

So, you don't try and inject controls. What happens now when you want to get that data back, say save it into the database? You now going to have a WHOLE set of controls that you have to have some numbering system to deal with.

Software not done this way. If you have repeating data, then use a repeating data control.

You can use repeater, listview, gridview. They all support repeating rows of data. The gridviewe or listview is for a grid layout. And the repeater would be the SAME idea, but you now laying out controls (repeating them) and you don't necessary have to use a grid format.

So, lets do a example of adding hotel names.

We have this grid markup:

<div style="float:left;margin-left:20px">


  <style> .borderhide input {border:none}</style>

  <asp:GridView ID="GridView1" runat="server" 
                AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table table-hover borderhide" >
  
      <Columns>
        <asp:TemplateField  HeaderText="HotelName" >
            <ItemTemplate><asp:TextBox id="HotelName" runat="server" Text='<%# Eval("HotelName") %>' /></ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField  HeaderText="FirstName" SortExpression="ORDER BY FirstName" >
            <ItemTemplate><asp:TextBox id="FirstName" runat="server" Text='<%# Eval("FirstName") %>' /></ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField  HeaderText="LastName" >
            <ItemTemplate><asp:TextBox id="LastName" runat="server" Text='<%# Eval("LastName") %>' /></ItemTemplate>
        </asp:TemplateField>

       <asp:TemplateField  HeaderText="City" >
            <ItemTemplate><asp:TextBox id="City" runat="server" Text='<%# Eval("City") %>' /></ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="Active">
            <ItemTemplate><asp:CheckBox id="Active" runat="server"  Checked = '<%# Eval("Active") %>'  /></ItemTemplate>
        </asp:TemplateField>
    </Columns>
  </asp:GridView>

    <div style="clear:both;padding-top:20px">
        <asp:Button ID="cmdSave" runat="server" Text="Save" />
        <asp:Button ID="cmdAdd" runat="server" Text="Add row" style="margin-left:20px" />
        <asp:Button ID="cmdUnDo" runat="server" Text="UnDo" Style="margin-left:20px"/>
    </div>
</div>

Not too much markup, but we GET to layout our repeating controls ONE time!!

Now, the code to fill this grid view is this:

Dim rstTable As New DataTable
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If IsPostBack = False Then

        LoadGrid()
        ViewState("rstTable") = rstTable
    Else
        rstTable = ViewState("rstTable")
    End If

End Sub


Sub LoadGrid()

    ' load up our drop down list from database (city)
    Dim strSQL As String

    strSQL = "SELECT ID, FirstName, LastName, HotelName, City, Active, Rating from tblHotels"

    Using cmdSQL As New SqlCommand(strSQL, New SqlConnection(My.Settings.TEST3))

        cmdSQL.Connection.Open()
        rstTable.Load(cmdSQL.ExecuteReader)

        GridView1.DataSource = rstTable
        GridView1.DataBind()

    End Using

End Sub

Clean, simple code, and the result is this:

enter image description here

Now, even more incredible? I can tab around in that grid - edit like Excel.

So now how do we send back the changes to the database (the ONE save button).

The code looks like this:

Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click

    ' pull repeater rows back to table.
    Dim bolDataOk As Boolean = True

    For Each rRow As GridViewRow In GridView1.Rows

        Dim RecordPtr As Integer = rRow.RowIndex
        Dim OneDataRow As DataRow

        OneDataRow = rstTable.Rows(RecordPtr)
        OneDataRow.Item("HotelName") = CType(rRow.FindControl("HotelName"), TextBox).Text
        OneDataRow.Item("FirstName") = CType(rRow.FindControl("FirstName"), TextBox).Text
        OneDataRow.Item("LastName") = CType(rRow.FindControl("LastName"), TextBox).Text
        OneDataRow.Item("City") = CType(rRow.FindControl("City"), TextBox).Text
        OneDataRow.Item("Active") = TryCast(rRow.FindControl("Active"), CheckBox).Checked

    Next

    ' now send table back to database with updates

    If bolDataOk Then

        Dim strSQL As String = "SELECT ID, FirstName, LastName, City, HotelName, Active from tblHotels WHERE ID = 0"

        Using cmdSQL As New SqlCommand(strSQL, New SqlConnection(My.Settings.TEST3))

            cmdSQL.Connection.Open()
            Dim daupdate As New SqlDataAdapter(cmdSQL)
            Dim cmdBuild As New SqlCommandBuilder(daupdate)

            daupdate.Update(rstTable)

        End Using

    End If

End Sub

So now, note how we can SAVE all the edits in that grid in ONE shot.

And if we decide to add more columns, or maybe even a button we hit to see/view one row, we can do so with great ease.

Lets wire up the add button (adds a row to the grid). When we press it, we get/have/add a new row to the grid.

So the code could be like this:

Protected Sub cmdAdd_Click(sender As Object, e As EventArgs) Handles cmdAdd.Click

    Dim MyOneRow = rstTable.NewRow

    ' set defaults for this new row
    MyOneRow("City") = "Edmonton"
    MyOneRow("Active") = True

    ' add htis new row to the table
    rstTable.Rows.Add(MyOneRow)

    ' now update the grid to show this new added row

    GridView1.DataSource = rstTable
    GridView1.DataBind()


End Sub

Wow! - was that not nice and easy to add a WHOLE new grid row?

So, how this "should" work if using desktop MS-Access, FoxPro, or even the web?

Use the controls and bits and parts that allow you to work with repeating data.

There is VERY little need to try and inject controls into the web page. But worse, for repeating sets of data? Then for sure you don't' want to try and manage, and code and try to deal with a "set" of controls who's only job is to display repeating data.

Study the above. I suggest you drop all those controls, the whacks of code, and adopt either a gridview, or even a repeater to manage this problem.

I mean, what is even worse?

What happens if you need to go back to that page to edit data? Now you have to pull the data, and now re-inject all those controls again. So, you NOW need two sets of code. One set to add the new controls, and then later on to go back and display/edit that data, you again have to write another loop to re-inject the controls for display.

edit --------------------------------------

So the follow up question was how to do this for NOT a grid.

Well as noted, we have a "choice" of some controls, and their job is to "repeat" data for us. As noted, for grids, then GridView and ListView are top choices.

But, for non grid, and just some layout (say some fields that is a form or some text boxes)?

Sure, that suggest a Repeter. Note how NEAR exact the same code approach as above works. The is the wonderful part about this approch. Once you do this one time with a grid, then you know and have the skills to do this with say the repeater.

So, say we have this:

enter image description here

Ok, so I might want 2, or 15 of them, right? So we take the layout for above, and put it "inside" of a repeater control.

It will look like this:

<asp:Repeater ID="Repeater1" runat="server">
    <ItemTemplate>

        <div style="border-style:solid;color:black;width:250px;float:left">
            <div style="padding:5px;text-align:right">
                Hotel Name: <asp:TextBox ID="HotelName" runat="server" Text ='<%# Eval("HotelName") %>' Width="130px" />
                <br />
                First Name: <asp:TextBox ID="txtFirst" runat="server" Text ='<%# Eval("FirstName") %>'  Width="130px" />
                <br />
                Last Name: <asp:TextBox ID="txtLast" runat="server" Text ='<%# Eval("LastName") %>'  Width="130px" />
                <br />
                City: <asp:TextBox ID="txtCity" runat="server" Text ='<%# Eval("City") %>'  Width="130px" />
                <br />
                Active: <asp:CheckBox ID="Active" runat="server" Checked = '<%# Eval("Active") %>'/>
            </div>
        </div>

        <div style="clear:both;height:20px">
            <!-- this div starts a new line -->
        </div>

    </ItemTemplate>
</asp:Repeater>

So, it really just the markup, but note how we used Eval(). This is a data expression - it ONLY works inside of grids, listview, repeater. However, it even works for say a Radio button list!

Ok, so the above is our markup. So, now the code to load up this repeater looks like this:

Dim rstTable As New DataTable

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not IsPostBack Then
        loadGrid()
        ViewState("MyTable") = rstTable
    Else
        rstTable = ViewState("MyTable")
    End If

End Sub

Sub loadGrid()

    Using cmdSQL As New SqlCommand("SELECT * FROM tblHotels ORDER BY HotelName", New SqlConnection(My.Settings.TEST3))

        cmdSQL.Connection.Open()
        rstTable.Load(cmdSQL.ExecuteReader)

        Repeater1.DataSource = rstTable
        Repeater1.DataBind()

    End Using

End Sub

And now we get this:

enter image description here

Now, I also dropped in two more plane jane buttons (add, and save).

I just dropped those buttion right after the repater (outside of repeater)

eg:

<br />
 <asp:Button ID="cmdAdd" runat="server" Text="Add new" />
 <asp:Button ID="cmdSave" runat="server" Text="Save" Style="margin-left:20px" />

If you edit ANY of the 3 - tab around - we can now hit save and again in ONE operation send all changes back to the database.

First, lets wire up the add button. When you hit it, then in place of 3 examples, you have 4. The code is thus this:

Protected Sub cmdAdd_Click(sender As Object, e As EventArgs) Handles cmdAdd.Click


    Dim MyOneRow = rstTable.NewRow

    ' set defaults for this new row
    MyOneRow("City") = "Edmonton"
    MyOneRow("Active") = True

    ' add this new row to the table
    rstTable.Rows.Add(MyOneRow)

    ' now update the grid to show this new added row
    Repeater1.DataSource = rstTable
    Repeater1.DataBind()


End Sub

Again, note how VERY clean and simple this code is.

So, if I just hit that add button, then I would see this:

enter image description here

But, I am 100% free to tab around and edit ANY of the 4 displayed.

So, now we need the save button. That will:

send back to database any changes/edits to existing
save + send back to database any NEW rows.
and if we added a delete button - it would do that for us to!!

The save button code is thus:

Protected Sub cmdSave_Click(sender As Object, e As EventArgs) Handles cmdSave.Click

    ' move data from each repeater back to table
    For Each rRow As RepeaterItem In Repeater1.Items

        Dim OneRow As DataRow = rstTable.Rows(rRow.ItemIndex)

        OneRow("FirstName") = CType(rRow.FindControl("txtFirst"), TextBox).Text
        OneRow("LastName") = CType(rRow.FindControl("txtLast"), TextBox).Text
        OneRow("HotelName") = CType(rRow.FindControl("HotelName"), TextBox).Text
        OneRow("City") = CType(rRow.FindControl("txtCity"), TextBox).Text
        OneRow("Active") = CType(rRow.FindControl("Active"), CheckBox).Checked

    Next

    ' now write the table back to database 

    Using cmdSQL As New SqlCommand("SELECT * FROM tblHotels WHERE ID = 0",
                    New SqlConnection(My.Settings.TEST3))
        Dim DA As New SqlDataAdapter(cmdSQL)
        Dim daUpdate As New SqlCommandBuilder(DA)

        DA.Update(rstTable)

    End Using


End Sub

So, we move data from Repeater back to table

Then we tell the system to update that table back to database.

So we did NOT have very much markup.

We were is REALLY great ease able to add a new row, but again let the repeater do all the work of showing that new data row.

Sign up to request clarification or add additional context in comments.

3 Comments

Wow thank you so much for taking the time to explain this to me. Very, very helpful. What I am doing is I am displaying X number of forms (based on how many checkboxlist items there are). So I need to display multiple forms the user fills out, rather than a grid with buttons to Add / Remove. Is it possible to make this grid look like a form and tell it a set number of forms to display? ( For Each i In CBLModifications.Items gets the number of items in the checkboxlist)
Ok, see my update. I show how this works WITH OUT a grid, but just some text boxes that we need to have "more" then one time. So look at the NEW part and answer I added to this
You are a lifesaver. My problem is resolved, and I successfully implemented it! :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.