【问题标题】:Adding a User Control dynamically to a FlowLayoutPanel将用户控件动态添加到 FlowLayoutPanel
【发布时间】:2016-11-11 12:26:46
【问题描述】:

我创建了一个 UserControl 并将其添加到 FlowLayoutPanel。 UserControl 用于允许用户在表单中输入材料、成本和交货状态。当用户填写完毕后,我希望另一个 UserControl 出现在其下方 FlowLayoutPanel

UserControl 只是根据输入到两个TextBox 控件的文本和两个Checkbox 控件的状态生成一个字符串。它还有一个属性,指示用户何时填写了足够的信息。我想用这个属性来表示生成一个新的 UserControl。

目前我在FlowLayoutPanel 上有我的第一个UserControl,它成功地将String 和CreateNew 属性传回。

我遇到的问题是:

  1. 如何监控CreateNew 是否已更改为 True?
  2. 如何将控件添加到表单并为控件名称 +1 以供将来引用
  3. 添加新控件后,我需要监视以查看新的CreateNew 状态是否更改以重复循环

任何人都可以在这里指出正确的方向吗,有很多关于这方面的信息,但我似乎无法从其他人的问题/问题中找到任何有用的信息。

更新 1

感谢用户 Zaggler 的评论,我现在已经设法控制在FlowLayoutPanel 上创建它自己的新实例。但是现在我面临一个新问题,它只创建一个新的用户控件,然后就停止了。

这是我正在使用的代码:

用户控制代码

Public Class Alv_Product_Order_Control
    Public OutString As String
    Public Event CreateNew()
    Dim CreateNewRaised As Boolean

    Private Sub OutputString(sender As Object, e As EventArgs) Handles tbMaterial.TextChanged, tbCost.TextChanged,
        cbDelivered.CheckedChanged, cbOrderPlaced.CheckedChanged

        OutString = "¦¦" & tbMaterial.Text & "¦" & tbCost.Text & "¦"

        If cbOrderPlaced.Checked = True Then
            OutString = OutString & "Yes¦"
        Else
            OutString = OutString & "No¦"
        End If

        If cbDelivered.Checked = True Then
            OutString = OutString & "Yes¦"
        Else
            OutString = OutString & "No¦"
        End If

        If tbCost.Text = "" Or tbMaterial.Text = "" Then
        Else
            If CreateNewRaised = False Then
                RaiseEvent CreateNew() 'Raise the event that's used to signal adding a new control to the layout
                CreateNewRaised = True 'Create A Latched Boolean that cannot change again in the future
            End If
        End If
    End Sub

    Public ReadOnly Property Alv_Product_Order_Control As String
        Get
            Return OutString 'Pass string back to main form
        End Get
    End Property

主窗体代码

Private Sub CreateSecondPOC() Handles POC1.CreateNew
    FlowLayoutPanel1.Controls.Add(New Alveare_VB.Alv_Product_Order_Control)
End Sub

我猜这里的问题是CreateSecondPOC 只处理第一个POC1 的事件

如何创建一个新的Alveare_VB.Alv_Product_Order_Control,将其命名为 POC2 并添加一个处理程序来处理POC2.CreateNew 并添加另一个控件?

编辑 2

我知道我找到了答案,但我想看看已经提到的内存泄漏。我已将以下答案中提供的代码更改为:

Private Sub CreateSecondPOC(ByVal sender As Object, ByVal e As System.EventArgs) Handles POC1.CreateNew
        Try
            Dim oldPoc = DirectCast(sender, Alveare_VB.Alv_Product_Order_Control)
            RemoveHandler oldPoc.CreateNew, AddressOf CreateSecondPOC
        Catch ex As Exception
            Debug.Print(ex.Message)
        End Try

        Dim newPoc As New Alveare_VB.Alv_Product_Order_Control
        AddHandler newPoc.CreateNew, AddressOf CreateSecondPOC
        FlowLayoutPanel1.Controls.Add(newPoc)
    End Sub

我在“RemoveHandler”行收到以下错误:

无法将“System.Windows.Forms.TextBox”类型的对象转换为“Alveare_VB.Alv_Product_Order_Control”类型。

CreateNew 事件在写入文本框时引发,这是作为我假设的发件人传回的?现在不知道该去哪里。

编辑 3

错误出现在我的UserControl 中,我发回了不正确的对象(在本例中为文本框)。我现在更改了 RaiseEvent 以将 UserControl 作为对象返回。现在一切正常。

【问题讨论】:

  • 创建一个事件并在需要时引发它。查看withevents...
  • @Zaggler 感谢您的指导,我已经取得了一些进展,请参阅更新的问题
  • 不客气,请参阅您接受的 Verdolino 答案下的评论。

标签: vb.net user-controls


【解决方案1】:

你可以把你的处理程序改成这样的

Private Sub CreateSecondPOC() Handles POC1.CreateNew
    Dim newPoc As New Alveare_VB.Alv_Product_Order_Control
    AddHandler newPoc.CreateNew, AddressOf CreateSecondPOC
    FlowLayoutPanel1.Controls.Add(newPoc)
End Sub

我不确定您是否要继续处理该事件,即使它已被填充一次,即是否可以取消填充,然后重新填充,然后再次引发事件?也许你想在它填充后锁定它,但这并不清楚。

您还可以将所有POC 控件保存在一个容器中,并且仅在它们全部填充后才创建一个新控件。

编辑:

根据下面的 cmets,您应该在不再需要事件处理程序时将其删除,以避免内存泄漏。这是一种方法

Private Sub CreateSecondPOC(sender As Object) Handles POC1.CreateNew
    Dim oldPoc = DirectCast(sender, Alveare_VB.Alv_Product_Order_Control)
    RemoveHandler oldPoc, AddressOf CreateSecondPOC
    Dim newPoc As New Alveare_VB.Alv_Product_Order_Control
    AddHandler newPoc.CreateNew, AddressOf CreateSecondPOC
    FlowLayoutPanel1.Controls.Add(newPoc)
End Sub

但是 last POC 控件的事件处理程序永远不会被取消订阅。所以你也可以在关闭表单时做这样的事情

For Each poc In Me.FlowLayoutPanel1.Controls.OfType(Of Alveare_VB.Alv_Product_Order_Control)()
    RemoveHandler poc.CreateNew, AddressOf CreateSecondPOC
Next poc

我在上面提到您还可以将所有POC 控件保存在一个容器中,这将是跟踪控件的更好方法,而不是将 FlowLayoutPanel 用作逻辑容器。只做对你有用的事情并考虑删除处理程序。

【讨论】:

  • 我确实锁定了事件,CreateNewRaised Bool 阻止事件再次引发,即使它被取消填充并重新填充。我试试看,谢谢
  • 领先我一步 - 我应该更仔细地查看您的代码 :)
  • @Verdolino 要记住的一件事是,如果动态添加处理程序,您也需要删除它们。如果没有,后果可能会发生。例如,如果不删除指向对象的指针会浮动并且 GC 找不到它,因此不会释放内存并导致内存泄漏.... With Events 为您处理这个,只是一个建议。
  • 如果对象被释放,那么为该对象在外部声明的事件将丢失,指针在释放时指向该对象。
  • 是的,你需要RaiseEvent.CreateNew(Me)
猜你喜欢
  • 1970-01-01
  • 2013-05-29
  • 2022-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-27
相关资源
最近更新 更多