2

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

在我的表单 (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. 直接表单调用。在这种情况下,该类直接调用表单的消息例程:

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


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

    **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 类处理该事件。在这种情况下,我们有一个 EventArg.vb 类,它在我们引发事件时被实例化。

    **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,其中涉及一个额外的类与场景 2。

因此,问题是......在这三种方法之间,哪一种是将消息从类返回到表单的“正确”方式?由于场景 2 也可以正常工作,因此是否需要根据场景 3 附加 EventArg 类?

提前谢谢了。

4

1 回答 1

1

我的回答是以上都不是。考虑这个例子

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

您有一个表单和一个类,并且该表单具有对该类的引用(该类甚至不知道该表单存在)。您的业​​务逻辑在类中执行,表单用于输入和显示信息。CountTo1000()直接从表单调用,这很糟糕,因为基本上 UI 线程被置于睡眠状态 1000 次,而该类试图通过在每次睡眠后引发事件来更新 UI。但是用户界面从来没有时间让事件发生,即被更新。放置Application.DoEvents()afterMe.Label1.Text = number.ToString()将允许 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 线程在其他方面什么都不做,并且可以处理由另一个线程引发的事件。

我更关注线程,但在这两个示例中,设计都有一个带有类的表单,表单知道类,但类不知道表单。更多信息可以在这里看到。

也可以看看:

于 2017-04-19T15:36:55.637 回答