3

我有两节课。

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)

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

4

3 回答 3

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 线程中显然不允许通过该自动全局实例变量(始终与类型共享相同的名称)使用表单。当你这样做时,它会抛出一个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 工作。

于 2013-11-12T13:29:03.030 回答
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

这对我有用。

于 2013-11-12T12:49:07.987 回答
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
于 2013-11-12T13:11:45.490 回答