跨线程操作无效 - 需要从异步线程使用和更新UI线程上的控件 [英] Cross-thread operation not valid - Need to use and update controls on UI thread from async thread

查看:196
本文介绍了跨线程操作无效 - 需要从异步线程使用和更新UI线程上的控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序构建报表文档,并且想要放置
例程来在后台工作者的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)
x.Start()
pre>

这不能解决我的问题,但给了我以下异常:


跨线程操作无效:从创建线程之外的线程访问控制'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 a ComboBox and a ProgressBar from another thread. You originally used BackgroundWorker which apparently either swallowed your cross-thread error or you swallowed it in a Try-Catch. Either way, you changed it to Thread 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 a Button 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆