跨线程操作无效 - 需要在异步线程的UI线程上使用和更新控件

时间:2016-05-19 18:06:55

标签: .net vb.net multithreading combobox backgroundworker

我有一个程序来构建报告文档,我想放置 例行程序根据" 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文档中的所有书签都会被填写,直到它到达位于&#34; DoWork&#34;底部的组合框引用。处理程序。有什么建议吗?

更新

正如所建议的那样,我尝试了线程同步......

    Dim x As New Thread(AddressOf buildReport)
    x.Start()

这并没有解决我的问题,但给了我以下例外:

  

跨线程操作无效:控制&#39; cmbName&#39;从创建它的线程以外的线程访问。

修:

 '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

1 个答案:

答案 0 :(得分:1)

澄清您遇到的问题:
您需要从另一个线程访问ComboBoxProgressBar。您最初使用的BackgroundWorker显然吞没了您的跨线程错误,或者您在Try-Catch中吞了它。无论哪种方式,您都将其更改为Thread并使“交叉线程操作无效”可见。

此错误&#34;跨线程操作无效&#34;当您尝试从其自己的线程访问用户控件时出现。能够修改这些控件很重要,那么我们该怎么做呢?

首先,修改要异步的方法以接受参数。这应该是一个对象,因此您可以将尽可能多的信息传递到任务所需的异步中。

以下是将对象作为参数包含的修改方法。

Public Sub buildReport(list_temp As Object)

在您的代码中,您传入的是ComboxBox文本,而不是对ComboBox的引用。这就是该部分确实有效的原因。然后传入对ProgressBar的引用。当您从异步方法访问进度条时,您没有调用委托。这意味着您必须在UI线程上创建一个更新控件的方法。然后,您声明将从异步方法调用的委托。

以下是启动更新TextBox文本的线程的按钮示例。对于此示例,您需要TextBoxButton

首先,您需要声明委托和该委托的实例。您还需要创建修改所需控件的方法,因为您需要将该方法名称传递给委托实例声明。

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线程调用此方法,因此您可以像往常一样访问控件。