ASP.NET - 两个用户控件 - 运行时添加的多个实例

时间:2013-04-26 19:47:57

标签: asp.net vb.net

我有一个带有两个用户控件的VB ASP.NET Web应用程序,每个用户控件包含一个文本输入。有两个提交按钮,每个按钮对应一个用户控件。

单击按钮可添加其相应用户控件的实例。在大多数情况下,这是有效的,除了在特定情况下,文本框的ID混合在一起,从而混合了先前输入的值。

问题场景如下:

1)单击 second 按钮(添加批准者按钮)两次,然后在两个结果文本框中输入一些值(为便于分析,使值不同)。

2)单击第一个按钮(“添加文档”按钮)一次。 (此处无需在结果文本框中添加任何值。)

此时一切正常。查看页面源,我看到两个“Approver”文本框的ID为ctl02_txtApprover和ctl03_txtApprover,而一个“Document”文本框的ID为ctl04_txtDocument。

  1. 再次单击第一个按钮(“添加文档”按钮)。
  2. 此时,第一个“审批者”文本框中的值消失。第二个“审批者”文本框中的值将迁移到第一个“审批者”文本框。查看页面源,两个“审批者”文本框的ID已更改为ctl03_txtApprover和ctl04_txtApprover。考虑到文本框ID已更改,迁移的值有意义。换句话说,ViewState显示正确,但控件ID不正确。

    我已尽可能简化代码并在此处发布。

    Default.aspx的

    <%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="WebApplicationUserControlTest._Default" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
            <asp:PlaceHolder ID="phDocument" runat="server" />
            <asp:Button ID="btnAddDocument" runat="server" Text="Add Document" />
            <br /><br />
            <asp:PlaceHolder ID="phApprover" runat="server" />
            <asp:Button ID="btnAddApprover" runat="server" Text="Add Approver" />
        </form>
    </body>
    </html>
    

    Default.aspx.vb

    Public Class _Default
    Inherits System.Web.UI.Page
    Private Const VIEWSTATE_DOCUMENT_COUNT As String = "DocumentCount"
    Private Const VIEWSTATE_APPROVER_COUNT As String = "ApproverCount"
    
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            ViewState(VIEWSTATE_DOCUMENT_COUNT) = 0
            ViewState(VIEWSTATE_APPROVER_COUNT) = 0
        Else
            're-display any preexisting dynamic sections on postback
            AddAllDocumentInfoSections()
            AddAllApproverSections()
        End If
    End Sub
    
    Protected Sub btnAddDocument_Click(sender As Object, e As EventArgs) Handles btnAddDocument.Click
        ViewState(VIEWSTATE_DOCUMENT_COUNT) += 1
        AddDocumentSection()
    End Sub
    
    Protected Sub btnAddApprover_Click(sender As Object, e As EventArgs) Handles btnAddApprover.Click
        ViewState(VIEWSTATE_APPROVER_COUNT) += 1
        AddApproverSection()
    End Sub
    
    Private Sub AddAllDocumentInfoSections()
        For i As Integer = 0 To ViewState(VIEWSTATE_DOCUMENT_COUNT) - 1
            AddDocumentSection()
        Next
    End Sub
    
    Private Sub AddAllApproverSections()
        For i As Integer = 0 To ViewState(VIEWSTATE_APPROVER_COUNT) - 1
            AddApproverSection()
        Next
    End Sub
    
    Private Sub AddDocumentSection()
        Dim c As UserControl = LoadControl("~/Document.ascx")
        phDocument.Controls.Add(c)
    End Sub
    
    Private Sub AddApproverSection()
        Dim c As UserControl = LoadControl("~/Approver.ascx")
        phApprover.Controls.Add(c)
    End Sub
    End Class
    

    Document.ascx

    <%@ Control Language="vb" AutoEventWireup="false" CodeBehind="Document.ascx.vb" Inherits="WebApplicationUserControlTest.Document" %><asp:TextBox ID="txtDocument" runat="server" /><br /><br />
    

    Approver.ascx

    <%@ Control Language="vb" AutoEventWireup="false" CodeBehind="Approver.ascx.vb" Inherits="WebApplicationUserControlTest.Approver" %><asp:TextBox ID="txtApprover" runat="server" /><br /><br />
    

    我使用的是Visual Studio 2010.目标框架是4.0。我试过更改clientIDMode,但这似乎没有什么区别。我是否遇到过.NET的错误或我的代码有问题?

2 个答案:

答案 0 :(得分:1)

这里的问题是您在初始化后正在修改Controls集合和ViewState。 您永远不应在Page Load事件中动态添加控件。

您需要在Page life cycle的Page_Init阶段添加控件,并从Page_Load事件中的else语句中删除代码。您的新Page_Init事件将如下所示:

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
    AddAllDocumentInfoSections()
    AddAllApproverSections()
End Sub

我相信您可能必须更改存储这些控件的“计数”的方式,因为此阶段尚未提供“视图状态”信息。在这种情况下,我只是将它存储为Session变量。您只需要使用“Session”在整个代码示例中更改对“ViewState”的引用,如下所示:

Private Sub AddAllDocumentInfoSections()
    For i As Integer = 0 To Session(VIEWSTATE_DOCUMENT_COUNT) - 1
        AddDocumentSection()
    Next
End Sub

答案 1 :(得分:1)

您的代码有问题。

如果动态地将控件添加到控件树中的同一命名容器中,则需要在每次回发后以相同的顺序添加它们。

在你的情况下,你不是这样做的。

在第2步,您按顺序添加了三个控件:

  • 审批人1(AddAllApproverSections)
  • 审批人2(AddAllApproverSections)
  • DocumentInfo 1(btnAddDocument_Click)

然后在回发后,您按以下顺序重新生成它们:

  • DocumentInfo 1(AddAllDocumentInfoSections)
  • 审批人1(AddAllApproverSections)
  • 审批人2(AddAllApproverSections)

因此控制ID不一样,而且你看到的问题也是如此。

一种解决方案可能是在ViewState中存储代表控件添加顺序的其他信息,以便您可以按相同的顺序重新创建它们。

但我可能倾向于采用不同的方法,例如将DocumentInfo部分放入Repeater的模板中,将Approver部分放入第二个Repeater中。每个Repeater都是绑定到合适集合的数据,添加项目(Approver或DocumentInfo)可以通过向相关集合添加元素并调用DataBind来实现。