跨线程操作无效 - 需要使用和更新来自异步线程的 UI 线程上的控件
Cross-thread operation not valid - Need to use and update controls on UI thread from async thread
我有一个构建报告文档的程序,我想将
在后台工作人员的“DoWork”处理程序下构建报告的例程。报告的初始部分已启动,但是,一旦我在组合框中引用选定的项目,它就会停止执行?
这是我的代码:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
ProgressBar1.Visible = True
Application.EnableVisualStyles()
ProgressBar1.Style = ProgressBarStyle.Marquee
ProgressBar1.MarqueeAnimationSpeed = 10
Dim x As New Thread(AddressOf buildReport)
x.Start()
MessageBox.Show("Build Complete")
ProgressBar1.Visible = False
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Sub
'builds the report
Public Sub buildReport()
Dim app As word.Application = New word.Application
Dim document As word.Document
Dim today As String()
app.Visible = True
document = app.Documents.Add("K:\ETL Test Files\" & mycallerPreview.previewInst.txtYear.Text & "\" & mycallerPreview.previewInst.txtVendor.Text & "\" & mycallerPreview.previewInst.txtReport.Text & "\Test Report\Report Data\ReportTemplate.doc") 'open up template
'document.Styles.Add("Contents1")
'document.Styles.Add("Contents2")
'document.Styles.Add("Contents3")
'add info to pre-made bookmarks
today = Date.Today.ToString.Split(" ")
document.Bookmarks("Date").Range.Text = today(0).ToString
document.Bookmarks("Date1").Range.Text = today(0).ToString
document.Bookmarks("Date2").Range.Text = today(0).ToString
document.Bookmarks("Date3").Range.Text = today(0).ToString
document.Bookmarks("Approver").Range.Text = mycallerPreview.mycallerSelect2.txtChecked.Text.ToString
document.Bookmarks("Number2").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Number1").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Vendor").Range.Text = mycallerPreview.previewInst.txtVendor.Text.ToString
document.Bookmarks("Test1").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks("TestTitle").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
If mycallerPreview.mycallerSelect2.cmbName.SelectedIndex <> -1 Then
document.Bookmarks("Writer").Range.Text = mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString
document.Bookmarks("Reviewer").Range.Text = mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString
End If
If mycallerPreview.mycallerSelect2.cmbQuote.SelectedIndex <> -1 Then
document.Bookmarks("Quote").Range.Text = mycallerPreview.mycallerSelect2.cmbQuote.SelectedItem.ToString
End If
我的 word 文档中的所有书签都已填写,直到它到达位于“DoWork”处理程序底部的组合框引用。有什么建议吗?
更新:
按照建议,我尝试了线程同步...
Dim x As New Thread(AddressOf buildReport)
x.Start()
这并没有解决我的问题,但出现了以下异常:
Cross-thread operation not valid: Control 'cmbName' accessed from a thread other than the thread it was created on.
修订:
'garbage collects and initializes progress bar to default values
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'create list of objects to pass through ThreadStart Method
Dim list As New List(Of Object)
If mycallerPreview.mycallerSelect2.cmbName.SelectedIndex <> -1 Then
list.Add(mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString)
End If
If mycallerPreview.mycallerSelect2.cmbQuote.SelectedIndex <> -1 Then
list.Add(mycallerPreview.mycallerSelect2.cmbQuote.SelectedItem.ToString)
End If
list.Add(ProgressBar1)
Dim x As New Thread(AddressOf buildReport)
x.Start(list)
MessageBox.Show("Build Complete")
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Sub
'builds the report
Public Sub buildReport(list_temp As Object)
Dim progress As New ProgressBar
progress = list_temp(2)
progress.Visible = True
Application.EnableVisualStyles()
progress.Style = ProgressBarStyle.Marquee
progress.MarqueeAnimationSpeed = 10
Dim list As List(Of Object) = list_temp
Dim app As word.Application = New word.Application
Dim document As word.Document
Dim today As String()
app.Visible = True
document = app.Documents.Add("K:\ETL Test Files\" & mycallerPreview.previewInst.txtYear.Text & "\" & mycallerPreview.previewInst.txtVendor.Text & "\" & mycallerPreview.previewInst.txtReport.Text & "\Test Report\Report Data\ReportTemplate.doc") 'open up template
'add info to pre-made bookmarks
today = Date.Today.ToString.Split(" ")
document.Bookmarks("Date").Range.Text = today(0).ToString
document.Bookmarks("Date1").Range.Text = today(0).ToString
document.Bookmarks("Date2").Range.Text = today(0).ToString
document.Bookmarks("Date3").Range.Text = today(0).ToString
document.Bookmarks("Approver").Range.Text = mycallerPreview.mycallerSelect2.txtChecked.Text.ToString
document.Bookmarks("Number2").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Number1").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Vendor").Range.Text = mycallerPreview.previewInst.txtVendor.Text.ToString
document.Bookmarks("Test1").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks("TestTitle").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks("Writer").Range.Text = list(0).ToString
document.Bookmarks("Reviewer").Range.Text = list(0).ToString
document.Bookmarks("Quote").Range.Text = list(1).ToString
澄清您遇到的问题:
您需要从另一个线程访问 ComboBox
和 ProgressBar
。您最初使用的 BackgroundWorker
显然吞没了您的跨线程错误,或者您在 Try-Catch
中吞下了它。无论哪种方式,您都将其更改为 Thread
并使 Cross Thread Operation Not Valid 可见。
当您尝试从非自身线程访问用户控件时,会出现此错误 "Cross-Thread Operation Not Valid"。能够修改这些控件很重要,那么我们该怎么做呢?
首先,您修改要异步的方法以接受参数。这应该是一个对象,以便您可以将任务所需的尽可能多的信息传递到异步中。
这是您修改后的方法,将对象作为参数包含在内。
Public Sub buildReport(list_temp As Object)
在您的代码中,您传入了 ComboxBox 文本而不是对 ComboBox 的引用。这就是该部分起作用的原因。然后您传递对 ProgressBar 的引用。当您从异步方法访问进度条时,您没有调用委托。这意味着您必须在 UI 线程上创建一个方法来更新您的控件。然后声明一个将从异步方法调用的委托。
下面是一个按钮示例,该按钮启动更新 TextBox 文本的线程。对于此示例,您需要一个 TextBox
和一个 Button
。
首先,您需要声明一个委托和该委托的一个实例。您还需要创建修改所需控件的方法,因为您需要将该方法名称传递到委托实例声明中。
Public Delegate Sub SetTextBoxDelegate(Text As String)
Public SetTextbox_UI_Thread As SetTextBoxDelegate = New SetTextBoxDelegate(AddressOf SetTextBox)
Public Sub SetTextBox(Text As String)
TextBox1.Text = Text
End Sub
现在这是启动线程的按钮点击:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim t As System.Threading.Thread = New Threading.Thread(AddressOf DoStuff)
t.Start()
End Sub
如您所见,它正在使用方法 DoStuff()
启动一个线程。这是调用我们的委托(如果需要)来更新文本框的方法。
Public Sub DoStuff()
System.Threading.Thread.Sleep(3000)
If TextBox1.InvokeRequired Then
TextBox1.Invoke(SetTextbox_UI_Thread, "Hello")
Else
TextBox1.Text = "Hello"
End If
End Sub
请注意,我首先检查了 InvokeRequired = True
,因为您可以从 UI 线程调用此方法,这样您就可以像往常一样访问控件。
我有一个构建报告文档的程序,我想将 在后台工作人员的“DoWork”处理程序下构建报告的例程。报告的初始部分已启动,但是,一旦我在组合框中引用选定的项目,它就会停止执行?
这是我的代码:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
ProgressBar1.Visible = True
Application.EnableVisualStyles()
ProgressBar1.Style = ProgressBarStyle.Marquee
ProgressBar1.MarqueeAnimationSpeed = 10
Dim x As New Thread(AddressOf buildReport)
x.Start()
MessageBox.Show("Build Complete")
ProgressBar1.Visible = False
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Sub
'builds the report
Public Sub buildReport()
Dim app As word.Application = New word.Application
Dim document As word.Document
Dim today As String()
app.Visible = True
document = app.Documents.Add("K:\ETL Test Files\" & mycallerPreview.previewInst.txtYear.Text & "\" & mycallerPreview.previewInst.txtVendor.Text & "\" & mycallerPreview.previewInst.txtReport.Text & "\Test Report\Report Data\ReportTemplate.doc") 'open up template
'document.Styles.Add("Contents1")
'document.Styles.Add("Contents2")
'document.Styles.Add("Contents3")
'add info to pre-made bookmarks
today = Date.Today.ToString.Split(" ")
document.Bookmarks("Date").Range.Text = today(0).ToString
document.Bookmarks("Date1").Range.Text = today(0).ToString
document.Bookmarks("Date2").Range.Text = today(0).ToString
document.Bookmarks("Date3").Range.Text = today(0).ToString
document.Bookmarks("Approver").Range.Text = mycallerPreview.mycallerSelect2.txtChecked.Text.ToString
document.Bookmarks("Number2").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Number1").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Vendor").Range.Text = mycallerPreview.previewInst.txtVendor.Text.ToString
document.Bookmarks("Test1").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks("TestTitle").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
If mycallerPreview.mycallerSelect2.cmbName.SelectedIndex <> -1 Then
document.Bookmarks("Writer").Range.Text = mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString
document.Bookmarks("Reviewer").Range.Text = mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString
End If
If mycallerPreview.mycallerSelect2.cmbQuote.SelectedIndex <> -1 Then
document.Bookmarks("Quote").Range.Text = mycallerPreview.mycallerSelect2.cmbQuote.SelectedItem.ToString
End If
我的 word 文档中的所有书签都已填写,直到它到达位于“DoWork”处理程序底部的组合框引用。有什么建议吗?
更新:
按照建议,我尝试了线程同步...
Dim x As New Thread(AddressOf buildReport)
x.Start()
这并没有解决我的问题,但出现了以下异常:
Cross-thread operation not valid: Control 'cmbName' accessed from a thread other than the thread it was created on.
修订:
'garbage collects and initializes progress bar to default values
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'create list of objects to pass through ThreadStart Method
Dim list As New List(Of Object)
If mycallerPreview.mycallerSelect2.cmbName.SelectedIndex <> -1 Then
list.Add(mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString)
End If
If mycallerPreview.mycallerSelect2.cmbQuote.SelectedIndex <> -1 Then
list.Add(mycallerPreview.mycallerSelect2.cmbQuote.SelectedItem.ToString)
End If
list.Add(ProgressBar1)
Dim x As New Thread(AddressOf buildReport)
x.Start(list)
MessageBox.Show("Build Complete")
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Sub
'builds the report
Public Sub buildReport(list_temp As Object)
Dim progress As New ProgressBar
progress = list_temp(2)
progress.Visible = True
Application.EnableVisualStyles()
progress.Style = ProgressBarStyle.Marquee
progress.MarqueeAnimationSpeed = 10
Dim list As List(Of Object) = list_temp
Dim app As word.Application = New word.Application
Dim document As word.Document
Dim today As String()
app.Visible = True
document = app.Documents.Add("K:\ETL Test Files\" & mycallerPreview.previewInst.txtYear.Text & "\" & mycallerPreview.previewInst.txtVendor.Text & "\" & mycallerPreview.previewInst.txtReport.Text & "\Test Report\Report Data\ReportTemplate.doc") 'open up template
'add info to pre-made bookmarks
today = Date.Today.ToString.Split(" ")
document.Bookmarks("Date").Range.Text = today(0).ToString
document.Bookmarks("Date1").Range.Text = today(0).ToString
document.Bookmarks("Date2").Range.Text = today(0).ToString
document.Bookmarks("Date3").Range.Text = today(0).ToString
document.Bookmarks("Approver").Range.Text = mycallerPreview.mycallerSelect2.txtChecked.Text.ToString
document.Bookmarks("Number2").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Number1").Range.Text = mycallerPreview.mycallerSelect2.txtReportNumber.Text.ToString
document.Bookmarks("Vendor").Range.Text = mycallerPreview.previewInst.txtVendor.Text.ToString
document.Bookmarks("Test1").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks("TestTitle").Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks("Writer").Range.Text = list(0).ToString
document.Bookmarks("Reviewer").Range.Text = list(0).ToString
document.Bookmarks("Quote").Range.Text = list(1).ToString
澄清您遇到的问题:
您需要从另一个线程访问 ComboBox
和 ProgressBar
。您最初使用的 BackgroundWorker
显然吞没了您的跨线程错误,或者您在 Try-Catch
中吞下了它。无论哪种方式,您都将其更改为 Thread
并使 Cross Thread Operation Not Valid 可见。
当您尝试从非自身线程访问用户控件时,会出现此错误 "Cross-Thread Operation Not Valid"。能够修改这些控件很重要,那么我们该怎么做呢?
首先,您修改要异步的方法以接受参数。这应该是一个对象,以便您可以将任务所需的尽可能多的信息传递到异步中。
这是您修改后的方法,将对象作为参数包含在内。
Public Sub buildReport(list_temp As Object)
在您的代码中,您传入了 ComboxBox 文本而不是对 ComboBox 的引用。这就是该部分起作用的原因。然后您传递对 ProgressBar 的引用。当您从异步方法访问进度条时,您没有调用委托。这意味着您必须在 UI 线程上创建一个方法来更新您的控件。然后声明一个将从异步方法调用的委托。
下面是一个按钮示例,该按钮启动更新 TextBox 文本的线程。对于此示例,您需要一个 TextBox
和一个 Button
。
首先,您需要声明一个委托和该委托的一个实例。您还需要创建修改所需控件的方法,因为您需要将该方法名称传递到委托实例声明中。
Public Delegate Sub SetTextBoxDelegate(Text As String)
Public SetTextbox_UI_Thread As SetTextBoxDelegate = New SetTextBoxDelegate(AddressOf SetTextBox)
Public Sub SetTextBox(Text As String)
TextBox1.Text = Text
End Sub
现在这是启动线程的按钮点击:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim t As System.Threading.Thread = New Threading.Thread(AddressOf DoStuff)
t.Start()
End Sub
如您所见,它正在使用方法 DoStuff()
启动一个线程。这是调用我们的委托(如果需要)来更新文本框的方法。
Public Sub DoStuff()
System.Threading.Thread.Sleep(3000)
If TextBox1.InvokeRequired Then
TextBox1.Invoke(SetTextbox_UI_Thread, "Hello")
Else
TextBox1.Text = "Hello"
End If
End Sub
请注意,我首先检查了 InvokeRequired = True
,因为您可以从 UI 线程调用此方法,这样您就可以像往常一样访问控件。