Vb.net 最佳实践-表单类从类模块接收消息
希望得到一些关于在表单上捕获实例化类返回的消息的最佳实践建议 在我的表单(form1.vb)中,我有一个反映正在进行的操作的标签,代码如下 在form1.vb中编码以显示消息:Vb.net 最佳实践-表单类从类模块接收消息,vb.net,winforms,code-standards,Vb.net,Winforms,Code Standards,希望得到一些关于在表单上捕获实例化类返回的消息的最佳实践建议 在我的表单(form1.vb)中,我有一个反映正在进行的操作的标签,代码如下 在form1.vb中编码以显示消息: Public Sub DisplayMessage(ByVal Msg as String, ByVal Show as Boolean) Application.DoEvents() If Show Then lblShow.Text = Msg lblShow.Refr
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
到目前为止,我遇到了三种方法:
form1.DisplayMessage("Show This Message", True)
场景1似乎是最简单的,尽管有人建议我不要这样做,并要求我提出一个事件。随着时间的推移,我遇到了场景3,它与场景2相比增加了一个类 因此,问题是。。。 在这三种方法之间,将消息从类返回到表单的“正确”方式是什么?场景3中的额外EventArg类是否必要,因为场景2也可以正常工作
非常感谢。我的回答不是上述任何一项。考虑这个例子
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。但是UI从来没有时间允许事件发生,即更新。在Me.Label1.Text=number.ToString()
之后放置Application.DoEvents()
,将允许用户界面更新。但这是糟糕设计的征兆。不要那样做
这里是另一个多线程的例子
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线程的任何方法调用。程序运行平稳,因为线程.Sleep
是在与UI线程不同的线程上完成的,不需要应用程序.DoEvents
,因为UI线程在其他方面什么也不做,并且可以处理由另一个线程引发的事件
我更关注线程,但在这两个示例中,设计都有一个带有类的表单,表单知道类,但类不知道表单。我们可以看到更多关于这方面的信息
另见:
- 为什么我们需要检查InvokeRequired,然后调用:
- 比现在的
更好的选择:线程
- 更酷的选择,如果你能把你的头围绕着它:
Application.DoEvents
、默认表单实例和非标准事件签名并不是任何最佳实践最坏实践的一部分:除非您是从不同的线程运行,否则您应该真正能够执行lblShow.Text=Msg
。首先为什么要使用应用程序.DoEvents
和刷新
?引发和处理事件与调用方法没有太大区别。如果在窗体持有引用的单独类中运行,那么您可能希望从该类引发事件,因为该类无法调用窗体上的方法。如果您是多线程的,即另一个类在非UI线程上执行长时间运行的方法,则需要在窗体的事件处理程序中处理标签更新的调用。因此,没有更多信息,答案并不简单。如果可以的话,我很乐意帮助更多人。@djv-干杯。我得到了这个想法,并将在我的代码中实现它。目前我开发的工具非常简单(通常是一个按钮,顺序脚本执行),但我对我的编码方式不满意。我会投票支持你的回答,但我似乎还没有投票权……现在有一个比Thread更好的选择:BackgroundWorker-在引入async await
后,它已经过时了
**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
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
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