最佳实践 - 表单 Class 从 Class 模块接收消息

Best Practices - Form Class Receiving Message from Class Modules

希望获得一些关于从我的表单上的实例化 class 捕获返回消息的最佳实践建议。

在我的表单 (form1.vb) 中,我有一个标签反映了正在做的事情,代码如下。

form1.vb 中显示消息的代码:

Public Sub DisplayMessage(ByVal Msg as String, ByVal Show as Boolean)
    Application.DoEvents()
    If Show Then
        lblShow.Text = Msg
        lblShow.Refresh()
    End If
End Sub

到目前为止我遇到过三种方法:

  1. 直接调用表单。 在这种情况下,class 直接调用表单的消息例程:

    form1.DisplayMessage("Show This Message", True)
    


  2. RaiseEvent within class. 在这种情况下,form1 是 class 发送消息的 Friends WithEvents,并且 class 将事件引发到窗体。

    **Declared in Form1.vb**
    Friend WithEvents Class1 as New Class1      
    
    **Declared in Class1.vb**
    Public Event SetMessage(ByVal Msg As String, ByVal Show As Boolean)
    
    **Used in Class1.vb**
    RaiseEvent SetMessage("Show This Message", True)
    


  3. 让 EventArgs class 处理事件。 在这种情况下,我们有一个 EventArg.vb class 这是每当我们引发事件时实例化。

    **Declared in Form1.vb**
    Friend WithEvents Class1 as New Class1    
    
    Private Sub class1_DisplayMessage(ByVal Msg As String, ByVal showAs Boolean, ByRef e As ProgressMessageEventArgs) Handles Class1.SetMessage
        DisplayMessage(Msg, Show)
    End Sub
    


    **Declared in Class1.vb**
    Public Event SetMessage(ByVal msg As String, ByVal Show As Boolean, ByRef e As ProgressMessageEventArgs)

    Protected Sub CaptureMessage(ByVal msg As String, ByVal Show As Boolean)
        RaiseEvent SetMessage(message, ShowList, New ProgressMessageEventArgs(message))
    End Sub

    **Used in Class1.vb**
    RaiseEvent CaptureMessage("Show This Message", True)


    **EventArg.vb created to handle ProgressMessageEventArgs class**
    Public NotInheritable Class ProgressMessageEventArgs
        Inherits System.EventArgs
        Public txt As String

        Public Sub New(ByVal txt As String)
            MyBase.New()
            Me.Text = txt 
        End Sub
    End Class


场景 1 似乎是最简单的,尽管有人建议我不要这样做,而是要求我提出一个事件。随着时间的推移,我遇到了场景 3,其中涉及额外的 class 与场景 2.

因此,问题是... 在这三种方法中,哪种方法是 "proper" 将消息从 class 返回到表单的方法?由于场景 2 也可以正常工作,因此场景 3 是否需要额外的 EventArg class?

非常感谢。

我的回答是上面的none。考虑这个例子

Public Class Form1

    Private WithEvents myClass1 As New Class1()

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        myClass1.CountTo1000()
    End Sub

    Private Sub MyClass1_Updated(number As Integer) Handles myClass1.Updated
        Me.Label1.Text = number.ToString()
    End Sub

End Class

Public Class Class1

    Public Event Updated(number As Integer)

    Public Sub CountTo1000()
        For i = 1 To 1000
            System.Threading.Thread.Sleep(1)
            RaiseEvent Updated(i)
        Next
    End Sub

End Class

您有一个表单和一个 class,并且该表单引用了 class(class 甚至不知道该表单的存在)。你的业务逻辑是在class中执行的,表单是用来输入和显示信息的。 CountTo1000() 直接从表单调用,这很糟糕,因为基本上 UI 线程被置于睡眠状态 1000 次,而 class 正在尝试更新 UI通过在每次睡眠后引发事件。但是 UI 从来没有时间让事件发生,即更新。在 Me.Label1.Text = number.ToString() 之后放置 Application.DoEvents() 将允许 UI 更新。但这是糟糕设计的症状。不要那样做。

这是另一个多线程的例子

Public Class Form1

    Private WithEvents myClass1 As New Class1()

    ' this handler runs on UI thread
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        ' make a new thread which executes CountTo1000
        Dim t As New System.Threading.Thread(AddressOf myClass1.CountTo1000)
        ' thread goes off to do its own thing while the UI thread continues
        t.Start()
    End Sub

    ' handle the event
    Private Sub MyClass1_Updated(number As Integer) Handles myClass1.Updated
        updateLabel(number.ToString())
    End Sub

    ' invoke on UI thread if required
    Private Sub updateLabel(message As String)
        If Me.Label1.InvokeRequired Then
            Me.Label1.Invoke(New Action(Of String)(AddressOf updateLabel), message)
        Else
            Me.Label1.Text = message
        End If
    End Sub

End Class

Public Class Class1

    Public Event Updated(number As Integer)

    Public Sub CountTo1000()
        For i = 1 To 1000
            System.Threading.Thread.Sleep(1)
            RaiseEvent Updated(i)
        Next
    End Sub

End Class

这个简单的示例展示了如何创建线程以及 运行 UI 中的一些代码。执行此操作时,如果必须访问 UI 控件 (Label1),则必须在 UI 上调用来自非 UI 线程的任何方法调用。程序 运行 很顺利,因为 Thread.Sleep 是在与 UI 线程不同的线程上完成的,不需要 Application.DoEvents,因为 UI 线程是否则什么也不做,并且可以处理其他线程引发的事件。

我更关注线程,但在两个示例中,设计都有一个带有 class 的表单,并且该表单知道 class,但 class 不知道了解表格。有关更多信息,请参见 here.

另请参阅: