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

问题描述

我将 Visual Studio 与 asp.net、vb.net 和 webforms 结合使用。

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

在我的按钮上单击 BtnStep4Next,我动态添加 ServiceForm.ascx(它是一个包含名字、姓氏、城市等的表单)。我将服务表单添加到列表中,并将列表存储在会话中。例如,可能有 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

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

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

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

软件没有这样做。如果您有重复数据,则使用重复数据控件。

您可以使用中继器、列表视图、网格视图。它们都支持重复的数据行。 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

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

enter image description here

现在,更不可思议了?我可以在该网格中切换 - 像 Excel 一样编辑。

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

代码如下:

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

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

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

让我们连接添加按钮(向网格添加一行)。当我们按下它时,我们会在网格中获取/拥有/添加一个新行。

所以代码可能是这样的:

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 甚至转发器来解决这个问题。

我的意思是,还有什么更糟的?

如果您需要返回该页面来编辑数据,会发生什么情况?现在您必须提取数据,然后再次重新注入所有这些控件。所以,你现在需要两套代码。一组用于添加新控件,然后返回并显示/编辑该数据,您再次必须编写另一个循环来重新注入控件以进行显示。

编辑--------------------------------------强>

所以后续问题是如何为非网格执行此操作。

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

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

当然,这建议使用 Repeter。请注意 NEAR 与上述完全相同的代码方法是如何工作的。这是这个方法的精彩部分。一旦您使用网格完成此操作一次,您就会知道并拥有使用中继器执行此操作的技能。

所以,假设我们有这个:

enter image description here

好的,所以我可能想要 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

现在我们明白了:

enter image description here

现在,我还添加了两个平面简按钮 (添加并保存)。

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

例如:

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

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

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

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

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

所以,如果我点击添加按钮,我会看到:

enter image description here

但是,我可以 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

因此,我们将数据从中继器移回表

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

所以我们没有太多的标记。

我们可以非常轻松地添加新行,但再次让转发器完成显示新数据行的所有工作。