2017-04-18 16 views
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.

因此,问题是...... 在这三种方法之间,这将是从类中返回消息的“正确”表格?根据场景3,额外的EventArg类是否必要,因为场景2也可以正常工作?

非常感谢提前。

+0

“最佳实践”取决于情况。三种形式中的任何一种都可以根据用例进行工作。但是,'Application.DoEvents',默认表单实例和非标准事件签名不是任何最佳实践的一部分 – Plutonix

+0

最差的做法:[Application.DoEvents()](http://stackoverflow.com/a/5183623/832052) – djv

+0

你应该真的能够做'lblShow.Text = Msg',除非你从另一个线程运行。为什么'Application.DoEvents'和'Refresh'放在首位? – djv

回答

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。但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线程的线程上完成的,因此该程序可以平稳运行,因为UI线程无需执行任何操作,并且可以处理其他线程引发的事件,因此不需要Application.DoEvents

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

参见:

+0

_现在比Thread更好的选择:BackgroundWorker_ - 在引入'async-await'之后是过时的选项 – Fabio