Reputation: 53
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?
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
Upvotes: 0
Views: 263
Reputation: 1473
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.
Upvotes: 1