跨线程操作无效 - 需要从异步线程使用和更新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)句柄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 b GC.WaitForPendingFinalizers()
End Sub
'构建报告
Public Sub buildReport()
Dim app As word.Application = New word.Application
Dim document As word.Document
Dim今天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)'打开模板
'document.Styles.Add(Contents1)
'document.Styles.Add Contents2)
'document.Styles.Add(Contents3)
向预先设置的书签添加信息
today = Date.Today.ToString.Split()
document.Bookmarks(Date)。Range.Text = today(0).ToString
document.Bookmarks(Date1)。Range.Text = today(0).ToString
document。书签(Date2)。Range.Text = today(0).ToString
document.Bookmarks(Date3)。Range.Text = today(0).ToString
document.Bookmarks ).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
文档。书签(Test1)。Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
document.Bookmarks(TestTitle)。Range.Text = mycallerPreview.mycallerSelect2.txtReportTitle.Text.ToString
如果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
如果
结束如果mycallerPreview.mycallerSelect2.cmbQuote.SelectedIndex<> -1 Then
document.Bookmarks(Quote)。Range.Text = mycallerPreview.mycallerSelect2.cmbQuote.SelectedItem.ToString
如果
我的word文档中的所有书签都被填充,直到到达位于DoWork处理程序底部的组合框引用。有任何建议吗?
更新:
建议使用线程同步...
Dim x As New Thread(AddressOf buildReport)
pre>
x.Start()
这不能解决我的问题,但给了我以下异常:
跨线程操作无效:从创建线程之外的线程访问控制'cmbName'。
修订:
'垃圾收集并将进度栏初始化为默认值
Private Sub Button2_Click(sender As Object,e As EventArgs )Handles Button2.Click
'创建要通过ThreadStart方法的对象列表
Dim list As New List(Of Object)
如果mycallerPreview.mycallerSelect2.cmbName.SelectedIndex<> -1 then
list.Add(mycallerPreview.mycallerSelect2.cmbName.SelectedItem.ToString)
如果
结束如果mycallerPreview.mycallerSelect2.cmbQuote.SelectedIndex<> -1 then
list.Add(mycallerPreview.mycallerSelect2.cmbQuote.SelectedItem.ToString)
结束如果
list.Add(ProgressBar1)
Dim x As New Thread (AddressOf buildReport)
x.Start(list)
MessageBox.Show(Build Complete)
GC.Collect()
GC.WaitForPendingFinalizers .Collect()
GC.WaitForPendingFinalizers()
End Sub
'构建报告
Public Sub buildReport(list_temp As Object)
Dim progress As新的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)'打开模板
'添加信息到预先设置的书签
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
中吞了它。无论哪种方式,您将其更改为线程
,并使交叉线程操作无效可见。
当您尝试从非自己的线程访问用户控件时,会出现跨线程操作无效错误。能够修改这些控件很重要,我们该怎么做呢?
首先,修改要同步的方法以接受参数。这应该是一个对象,所以你可以传递尽可能多的信息到任务所需的异步。
这是你修改的方法,将对象作为参数。
Public Sub buildReport(list_temp As Object)
在您的代码中,您在ComboxBox文本中传递而不是ComboBox的引用。这就是为什么这部分工作。然后你传递一个对你的ProgressBar的引用。当您从异步方法访问progressbar时,您没有调用委托。这意味着你必须在UI线程上创建一个更新控件的方法。然后声明一个将从async方法调用的委托。
这里是一个按钮的示例,该按钮开始更新TextBox文本的线程。此示例需要
TextBox
和按钮
。
首先,您需要声明一个委托和该委托的一个实例。您还需要创建修改所需控件的方法,因为您需要将该方法名称传递给委托实例声明。
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
现在这里是按钮点击启动线程: p>
Private Sub Button2_Click(sender As Object,e As EventArgs)句柄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)
如果TextBox1.InvokeRequired然后
TextBox1.Invoke(SetTextbox_UI_Thread,Hello)
Else
TextBox1.Text =Hello
End If
End Sub
注意,我首先检查
InvokeRequired = True
,因为您可以从UI线程调用此方法,因此您可以像平常一样访问控件。I have a program that builds report documents and am wanting to place the routine to build the report under a "DoWork" handler for a background worker. The initial part of the report is started, however, once I reference selected items in a combo box it stops executing?
Here is my code:
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
All bookmarks in my word document are filled in until it reaches the combo box references which are at the bottom of the "DoWork" handler. Any suggestions?
Update:
As suggested, I tried thread synchronization ...
Dim x As New Thread(AddressOf buildReport) x.Start()
This doesn't solve my problem but gave me the following exception:
Cross-thread operation not valid: Control 'cmbName' accessed from a thread other than the thread it was created on.
Revised:
'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
解决方案To clarify the issue you are having:
You need to access aComboBox
and aProgressBar
from another thread. You originally usedBackgroundWorker
which apparently either swallowed your cross-thread error or you swallowed it in aTry-Catch
. Either way, you changed it toThread
and made the Cross Thread Operation Not Valid visible.This error "Cross-Thread Operation Not Valid" arises when you try to access a user control from a thread other than its own. It's important to be able to modify these controls so how do we do it?
First, you modify the method you want to be async to accept a parameter. This should be an object so you can pass as much information into your async that will be needed for the task.
Here is your modified method to include the object as a parameter.
Public Sub buildReport(list_temp As Object)
In your code you passed in the ComboxBox text rather than a reference to the ComboBox. This is why that part does work. Then you pass in a reference to your ProgressBar. When you accessed the progressbar from your async method, you did so without invoking a delegate. What this means is that you have to create a method on the UI thread that updates your control. You then declare a delegate that will be called from the async method.
Here is an example of a button starting a thread that updates the TextBox text. You'll need a
TextBox
and aButton
for this example.First, you need to declare a delegate and an instance of that delegate. You'll also need to create the method that modifies the control you want because you need to pass that method name into the delegate instance declaration.
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
Now here is the button click which starts the thread:
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
As you can see, it is starting a thread using the method
DoStuff()
. This is the method that invokes our delegate (if needed) to update the textbox.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
Note that I first checked if
InvokeRequired = True
because you can call this method from the UI thread so then you could just access the controls as you normally would.这篇关于跨线程操作无效 - 需要从异步线程使用和更新UI线程上的控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!