问题是你正在使用全局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 工作。