【问题标题】:Cross-thread operation not valid - Need to use and update controls on UI thread from async thread跨线程操作无效 - 需要从异步线程使用和更新 UI 线程上的控件
【发布时间】:2016-05-19 18:06:54
【问题描述】:

我有一个构建报告文档的程序,我想将 例程在后台工作人员的“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()

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

跨线程操作无效:控件“cmbName”从创建它的线程以外的线程访问。

修订:

 '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

【问题讨论】:

  • 您遇到异常了吗? SelectedItem 可能不会导致 NRE - 但您没有检查。与其引用不同线程上的控件,不如使用DoWorkEventArgs 将所需的任何数据传递给工作人员
  • 不抛出异常。 & 我会试试看谢谢!
  • BackgroundWorker 不错,但我更喜欢在设置异步方法时使用 Thread 和 ThreadStart。
  • 试过了。在我的问题中更新了它。
  • 刚刚将 [ComboBox] [Selected Items] 作为对象列表传递给线程启动方法。谢谢你的帮助!!

标签: .net vb.net multithreading combobox backgroundworker


【解决方案1】:

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

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

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

这是您修改后的方法,将对象作为参数包含在内。

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

【讨论】:

  • 与您给出的示例类似,我为我的问题传递了一个 ProgressBarStyle !非常感谢!
  • 我正要写另一个从另一个线程更新进度条的例子。很高兴您能够使用此示例来更新进度条。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 2011-01-15
相关资源
最近更新 更多