从动态创建的文本框中获取用户的文本框输入

Grabbing user's textbox input from dynamically created textboxes

我将 Visual Studio 与 asp.net、vb.net 和网络表单一起使用。

我的问题是我需要从动态生成的文本框中获取用户的文本框输入。

在我的按钮上单击 BtnStep4Next,我动态添加 ServiceForm.ascx(这是一个包含 FirstName、LastName、City 等的表单)。我将服务表单添加到列表中,该列表存储在会话中。例如,可能有 5 个网络表单 (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

点击不同的按钮,现在我想获取用户在文本框中输入的内容。我的代码虽然会导致空字符串(原始表单文本)而不是用户更新的输入。我怎样才能真正获得用户此时输入的每个文本框的文本?

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

你写的代码太多了。我的意思是,如果我需要 10 个变量,我会去

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

没有! 你这样做:

dim MyValues(10) as integer

我们使用数组的集合。我的意思是甚至在桌面软件上。如果我需要一堆重复数据行 - 你可以使用网格、连续形式或任何其他重复类型的控件来为你完成所有这些肮脏的工作。

说,我想输入一些酒店名称。但是,提前我不知道有多少。我的意思是,任何数据库系统+问题都必须处理这种情况。

因此,您不要尝试注入控件。当您想取回该数据时会发生什么,比如说将其保存到数据库中?您现在将拥有一整套控件,您必须使用一些编号系统来处理这些控件。

软件不是这样做的。如果您有重复数据,请使用重复数据控件。

你可以使用repeater、listview、gridview。它们都支持重复的数据行。 gridviewe 或 listview 用于网格布局。中继器是相同的想法,但您现在布置控件(重复它们)并且您不必使用网格格式。

所以,让我们做一个添加酒店名称的例子。

我们有这个网格标记:

<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>

没有太多标记,但我们可以一次布置我们的重复控件!!

现在,填充此网格视图的代码是这样的:

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

干净、简单的代码,结果是这样的:

现在,更不可思议?我可以在该网格中切换 - 像 Excel.

一样编辑

那么现在我们如何将更改发送回数据库(一个保存按钮)。

代码如下所示:

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

现在,请注意我们如何一次性保存该网格中的所有编辑。

如果我们决定添加更多列,或者甚至是我们点击到 see/view 一行的按钮,我们可以轻松完成。

让我们连接添加按钮(向网格添加一行)。当我们按下它时,我们 get/have/add 一个新行到网格。

所以代码可以是这样的:

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

哇! - 添加一个全新的网格行不是很好很容易吗?

那么,如果使用桌面 MS-Access、FoxPro 甚至网络,这个“应该”如何工作?

使用允许您处理重复数据的控件、位和部件。

几乎不需要尝试将控件注入网页。但更糟糕的是,对于重复的数据集?那么您肯定不想尝试和管理、编写代码并尝试处理“一组”控件,这些控件的唯一工作就是显示重复数据。

学习以上内容。我建议您放弃所有这些控件和代码,采用 gridview,甚至是 repeater 来解决这个问题。

我的意思是,更糟糕的是什么?

如果您需要返回该页面编辑数据会怎样?现在您必须提取数据,现在 re-inject 所有这些控件再次出现。所以,你现在需要两套代码。一组用于添加新控件,然后返回 display/edit 该数据,您必须再次编写另一个循环以 re-inject 控件进行显示。

编辑-------------------------------- ----

因此后续问题是如何针对非网格执行此操作。

如前所述,我们可以“选择”一些控件,它们的工作是为我们“重复”数据。如前所述,对于网格,GridView 和 ListView 是首选。

但是,对于非网格,只是一些布局(比如一些字段是表单或一些文本框)?

当然,这表明 Repeter。请注意 NEAR 与上面完全相同的代码方法是如何工作的。这是关于此方法的精彩部分。一旦你用网格做了一次,你就会知道并有能力用中继器来做这件事。

所以,假设我们有这个:

好的,所以我可能想要 2 个或 15 个,对吗? 所以我们采用上面的布局,并将其放在转发器控件的“内部”。

它看起来像这样:

<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>

所以,它实际上只是标记,但请注意我们如何使用 Eval()。这是一个数据表达式——它只适用于网格、列表视图、转发器。但是,它甚至适用于单选按钮列表!

好的,以上就是我们的标记。所以,现在加载这个中继器的代码如下所示:

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

现在我们明白了:

现在,我还投了两个平面简纽扣 (添加并保存)。

我刚刚在中继器之后(在中继器之外)放下了那些按钮

例如:

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

如果您编辑 3 个选项卡中的任何一个 - 我们现在可以点击保存并在一个操作中再次将所有更改发送回数据库。

首先,让我们连接添加按钮。当你点击它时,你有 4 个例子而不是 3 个例子。代码是这样的:

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

再一次,请注意非常干净这段代码很简单。

所以,如果我只是点击那个添加按钮,那么我会看到这个:

但是,我有 100% 的自由选择和编辑显示的 4 个中的任何一个。

所以,现在我们需要保存按钮。那将:

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!!

保存按钮代码如下:

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

因此,我们将数据从 Repeater 移回 table

然后我们告诉系统将 table 更新回数据库。

所以我们没有太多标记。

我们真的很容易添加新行,但再次让转发器完成显示新数据行的所有工作。