我在VSTO / VB中有一个使用BackgroundWorker工作正常的项目。它是一个需要信息网页的表单。网页可能需要一段时间,因此我使用BackgroundWorker调用表单。
然后我有一个添加了BackgroundWorker项目的Excel Addin项目。当我从Excel Addin项目调用表单并使用BackgroundWorker请求网页时,它可以抓取网页。但是在BackgroundWorker1_RunWorkerCompleted方法完成后完成的工作会导致错误消息:
“跨线程操作无效:控制'TabPage2'从其创建的线程以外的线程访问。”
为什么从Excel Addin项目调用后,BackgroundWorker项目不起作用?
我注意到当我将BackgroundWorker项目设置为“启动项目”时,没有生成错误。它与从Excel Addin项目调用此BackgroundWorker项目有关。
答案 0 :(得分:1)
编辑:可能是我从非UI线程调用RunWorkerAsync()吗?
我有一个带有Ribbon类的Excel Addin项目。 Ribbon1.vb有一个按钮单击方法,用于创建第二个项目的实例,将从中调用后台工作程序:
Private Sub Btn_Click(ByVal sender As System.Object, ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) Handles Btn.Click
Dim MySecondProject As SecondProject.Form1 = New SecondProject.Form1()
MySecondProject.Show()
End Sub
MySecondProject然后在其自己的按钮单击方法中调用BackgroundWorker:
BackgroundWorker1.RunWorkerAsync()
然后,完成后,后台工作者正在尝试更新MySecondProject中的标签:
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
SuccessLabel.Text = "Success!"
End Sub
当MySecondProject是启动项目时,后台工作人员会跟踪正确的线程并在完成后成功更新标签。使用Excel Addin作为启动项目,并在运行时实例化MySecondProject,后台工作人员似乎忘记了正确的线程。我应该手动在某处插入Invoke或BeginInvoke来帮助后台工作人员跟踪要更新的正确线程吗?
答案 1 :(得分:1)
因此,事实证明,从Ribbon启动的Form启动的Backgroundworker无法在处理结束时更新控件。我不确定为什么它可以从作为Startup项目启动的Form中工作,而它不能从Ribbon启动的表单中工作,但事实证明,你需要处理后台工作者失去UI线程的轨道
使用MethodInvoker,如下面的代码段所示:
Imports System.Windows.Forms
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
[do nothing]
End Sub
Private myString As String = "This is my string"
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
If Label1.InvokeRequired Then
Dim mi As MethodInvoker = AddressOf UpdateFormText
Label1.BeginInvoke(mi)
Else
Label1.Text = myString
End If
End Sub
Private Sub UpdateFormText()
Label1.Text = myString + " (BeginInvoked)"
End Sub
End Class
VS2010中更好的答案是使用内联MethodInvoker而不是第二个函数:
Me.Invoke(CType(Sub() Me.Label1.Text = "This is my string", MethodInvoker))