使用另一个类的backgroundworker从mainform类更新标签

时间:2013-11-12 12:34:47

标签: vb.net backgroundworker

我有两节课。

Public Class MainForm

     Private Project As clsProject


Private Sub btnDo_Click
   ...
   Backgroundworker.RunWorkerAsync()

End Sub
 Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork


    Project = New clsProject


End Sub

和MainForm中的两个方法

 Public Shared Sub setLabelTxt(ByVal text As String, ByVal lbl As Label)
    If lbl.InvokeRequired Then
        lbl.Invoke(New setLabelTxtInvoker(AddressOf setLabelTxt), text, lbl)
    Else
        lbl.Text = text
    End If
End Sub
Public Delegate Sub setLabelTxtInvoker(ByVal text As String, ByVal lbl As Label)
end class

我想从clsProject构造函数更新MainForm的标签。

 MainForm.setLabelTxt("Getting prsadasdasdasdasdry..", MainForm.lblProgress)

但它不会更新它们。 我做错了什么?

3 个答案:

答案 0 :(得分:2)

问题是您正在使用全局MainForm实例来访问后台线程中的标签:

Public Class clsProject
    Public Sub New()
        ' When accessing MainForm.Label1 on the next line, it causes an exception
        MainForm.setLabelTxt("HERE!", MainForm.Label1)
    End Sub
End Class

调用MainForm.setLabelTxt是可以的,因为这是一个共享方法,所以它不会通过全局实例来调用它。但是,当您访问Label1属性时,那就是利用VB.NET的技巧来访问表单的全局实例。在非UI线程中显然不允许通过该auto-global-instance变量(总是与该类型共享同一名称)使用该表单。执行此操作时,它会抛出InvalidOperationException,并显示以下错误消息:

  

创建表单时出错。有关详细信息,请参阅Exception.InnerException。错误是:ActiveX控件'8856f961-340a-11d0-a96b-00c04fd705a2'无法实例化,因为当前线程不在单线程单元中。

我猜你没有看到错误的原因是因为你正在某个地方捕获异常并且你只是忽略它。如果您停止使用该全局实例变量,则错误消失并且可以正常工作。例如,如果您将构造函数更改为:

Public Class clsProject
    Public Sub New(f As MainForm)
        ' The next line works because it doesn't use the global MainForm instance variable
        MainForm.setLabelTxt("HERE!", f.Label1)
    End Sub
End Class

然后,在MainForm中,你必须这样称呼它:

Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    Project = New clsProject(Me)   ' Must pass Me
End Sub

不允许使用后台线程中的全局实例,但是当我们使用后台线程中的相同标签时,不经过该全局变量就可以工作。

所以很明显你不能使用后台线程中的全局MainForm变量,但可能不清楚的是,永远使用它是个坏主意。首先,它令人困惑,因为它与MainForm类型的名称相同。更重要的是,它是一个全局变量,如果可以避免的话,任何形式的全球状态几乎总是不好的做法。

虽然上面的例子确实解决了这个问题,但它仍然是一种非常糟糕的方式。更好的选择是将setLabelTxt方法传递给clsProject对象,或者更好的是clsProject只需在需要更改标签时引发事件。然后,MainForm可以简单地监听这些事件并在它们发生时处理它们。最终,clsProject类可能是某种业务类,无论如何都不应该进行任何类型的UI工作。

答案 1 :(得分:1)

尝试:

Me.Invoke(...)

而不是lbl.Invoke(...。我不得不这样做。这是我的实施:

Delegate Sub SetTextDelegate(ByVal args As String)

Private Sub SetTextBoxInfo(ByVal txt As String)
    If txtInfo.InvokeRequired Then
        Dim md As New SetTextDelegate(AddressOf SetTextBoxInfo)
        Me.Invoke(md, txt)
    Else
        txtInfo.Text = txt
    End If
End Sub

这对我有用。

答案 2 :(得分:1)

您无法直接从BackgroundWorker对GUI元素执行任何操作。 “克服”的一种方法是强制通过Me.Invoke从主线程执行给定的动作;但这不是理想的程序。此外,您的代码混合了主窗体和外部类(+共享/非共享对象),这使得整个结构不太稳固。

一个可靠的工作解决方案依赖于特定的BGW方法来处理GUI元素;例如:ProgressChanged Event。示例代码:

Public Class MainForm
    Private Project As clsProject
    Public Shared bgw As System.ComponentModel.BackgroundWorker

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        bgw = BackgroundWorker1 'Required as far as you want to called it from a Shared method

        BackgroundWorker1.WorkerReportsProgress = True
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Project = New clsProject
    End Sub

    Public Shared Sub setLabelTxt(ByVal text As String)
        bgw.ReportProgress(0, text) 'You can write any int as first argument as far as will not be used anyway
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Me.Label1.Text = e.UserState 'You can access the given GUI-element directly
        Me.Label1.Update()
    End Sub
End Class

Public Class clsProject
    Public Sub New()
        MainForm.setLabelTxt("Getting prsadasdasdasdasdry..")
    End Sub
End Class