VB 控件的 .NET 事件处理程序

VB .NET event handler of controls

我正在使用 VB .NET 来创建计划,但我在活动方面遇到了一点问题。 在主窗体中,我放置了一个面板,在其中我以编程方式在这些行中添加行和框。我在表单中有一个文本框和包含所有框的面板。我想在单击某个框时更改 TextBox 的文本,因此我使用了 AddHandler 语句,但它不起作用。我试着调试它,我意识到它实际上调用了 sub 并在其中,我可以看到它所做的更改(TextBox.Text 变成了我想要的),但是当它退出 sub 时,就好像什么都没有改变. 我不知道我是否足够清楚。 谢谢

这是一个简化的代码(我删除了所有图形功能以调整控件的大小...)

Public Class frmPrinc
    Public actEditing As Object

    Private Class boxAct
        Inherits Label

        Public act As Integer

        Public Sub New(ByVal a As Integer)
            act = a

            AddHandler Me.Click, AddressOf clickBox
        End Sub

        Private Sub clickBox(sender As Object, e As EventArgs)
            Dim boxact As boxAct = DirectCast(sender, boxAct)
            frmPrinc.actEditing = boxact
            boxact.Text = "Clicked"
        End Sub
    End Class

    Private Sub showPlanning()
        pan_plan.Controls.Clear()
        Dim plan As New Control ' Control that will be used as a row
        For i As Integer = 0 To 10
            plan.Controls.Add(New boxAct(i))
        Next
        Panel1.Controls.Add(plan)
    End Sub
End Class

当我 运行 那个时候,框的文本发生了变化,但 actEditing 仍然没有...

boxAct 不会尝试直接 更新当前 "box" 被点击的 frmPrinc,它应该引发一个 自定义事件 frmPrinc 订阅。 frmPrinc 可以在它认为合适的时候使用该信息。下面我添加了自定义事件并在 class boxAct 中引发它。创建 boxAct 的每个实例时,表单使用 AddHandler 订阅该事件。总之,这看起来像:

Public Class frmPrinc

    Public actEditing As boxAct

    Public Class boxAct
        Inherits Label

        Public act As Integer
        Public Event BoxClicked(ByVal box As boxAct)

        Public Sub New(ByVal a As Integer)
            act = a
        End Sub

        Private Sub boxAct_Click(sender As Object, e As EventArgs) Handles Me.Click
            Me.Text = "Clicked"
            RaiseEvent BoxClicked(Me)
        End Sub

    End Class

    Private Sub showPlanning()
        pan_plan.Controls.Clear()
        Dim plan As New Control ' Control that will be used as a row
        For i As Integer = 0 To 10
            Dim box As New boxAct(i)
            AddHandler box.BoxClicked, AddressOf box_BoxClicked
            plan.Controls.Add(box)
        Next
        Panel1.Controls.Add(plan)
    End Sub

    Private Sub box_BoxClicked(box As boxAct)
        actEditing = box
        Debug.Print("Box Clicked: " & actEditing.act)
    End Sub

End Class

来自评论:

Thanks man, it worked! I'd like to know though why I need to make such a structure to raise a simple event that modifies the main form... Just to not do the same mistake again – Algor Frile

这是每个人都必须做出的设计决定:"Loosely Coupled" vs. "Tightly Coupled"。我上面给出的方法属于 Loosely Coupled 类别。松耦合解决方案的主要好处是 可重用性 。在您的具体情况下,我们有 Class boxAct 被多次重复使用,尽管都是在同一个表单中。但如果不是这样呢?如果您想在多个表单上使用 boxAct(甚至有多个 "groups")怎么办?用你原来的方法,你有这条线:

frmPrinc.actEditing = boxact

这意味着如果想以不同的形式使用 Class boxAct,您必须复制 Class boxAct,给它一个新名称,然后手动更改那个引用新表格的行:

Public Class boxAct2

    Private Sub clickBox(sender As Object, e As EventArgs)
        Dim boxact As boxAct = DirectCast(sender, boxAct)
        frmSomeOtherForm.actEditing = boxact
        boxact.Text = "Clicked"
    End Sub

End Class

这显示了紧耦合方法的缺点,它使用对特定类型(在本例中为表单)的引用来进行通信.当您快速而激烈地编码时,紧耦合方法最初可能更容易实现,但随后它会受到可重用性的影响。在某些情况下,紧密耦合的解决方案是有意义的,但只有您才能做出决定;这些场景通常涉及某种 "sub-control",只会在某种自定义 container/control 中使用,永远不会在其他地方单独使用。

相反,对于松散耦合的方法,如果我们想以不同的形式重用 Class boxAct,则 无需更改全部(尽管那时您可能不希望它在您的原始表单中声明!)。在新表单中,您只需为 BoxClicked() 事件添加一个处理程序,然后执行您需要执行的操作。每个表单都会收到其各自的 boxAct 实例的事件。

最后的想法......你原来的方法实际上可以工作,但很可能在这一行失败(与上面相同):

frmPrinc.actEditing = boxact

此处您使用该形式的所谓 Default Instance 引用 frmPrinc。如果 frmPrinc 是您应用程序的 "Startup Object",这将有效。然而,我猜它不是,你正在从其他地方创建一个 frmPrinc 实例。要使原始方法起作用,您必须将对 frmPrinc 的实际实例的引用传递到 Class boxAct(通常通过紧耦合解决方案中的构造函数)。