Rick Becker
asked on
Backgroundworker fails in VB.net with "Cross-Thread operation not valid"
Hi All,
Ok I have a Function that needs to run in the background from the Same UI as the rest of the Program. I am using Visual Studio 2017 and VB.net.
I have been able to get most of the Function working in the background using the Backgroundworker control. This is a Very large function and it DOES need to periodically output some data to a TEXTBOX during its operation.
I know that I need to setup DELEGATES to workaround this Cross-Thread problem but I am only partially successful in my endeavors. With the Delegate that I created I can only write to one TEXTBOX but not another..
So in my research I find that there is a method of turning off the Checking of this condition.. I see that the option that I must set is 'Control.CheckForIllegalCr ossThreadC alls. I understand that this is not recommended but my problem is that I Can Not find this Option to set...
Also if I try to use the 'INVOKE' method I cannot find the 'InvokeRequired' method which I believe is the reason why my DELEGATE is only partially successful.
I would be grateful for help in either finding these options or in getting my delegates to work properly.
Obviously I will provide as much of my code as you wish to see upon request.
Thanks in advance
Rick
Ok I have a Function that needs to run in the background from the Same UI as the rest of the Program. I am using Visual Studio 2017 and VB.net.
I have been able to get most of the Function working in the background using the Backgroundworker control. This is a Very large function and it DOES need to periodically output some data to a TEXTBOX during its operation.
I know that I need to setup DELEGATES to workaround this Cross-Thread problem but I am only partially successful in my endeavors. With the Delegate that I created I can only write to one TEXTBOX but not another..
So in my research I find that there is a method of turning off the Checking of this condition.. I see that the option that I must set is 'Control.CheckForIllegalCr
Also if I try to use the 'INVOKE' method I cannot find the 'InvokeRequired' method which I believe is the reason why my DELEGATE is only partially successful.
I would be grateful for help in either finding these options or in getting my delegates to work properly.
Obviously I will provide as much of my code as you wish to see upon request.
Thanks in advance
Rick
The BackgroundWorker has a ProgressChanged event which can be used to update the UI. From your background worker, you would call the ReportProgress() method, and that would fire the ProgressChanged event. In the event handler, you can update the UI without cross-threading issues. You can pass an Object into the ReportProgress() method and that object is in turn passed to the ProgressChanged event. You can access the properties of that object to update your UI
Hi Rick;
You need to implement the BackgroundWorker.ProgressC hanged Event, That event runs in the same thread as the UI and so you will not get that exception. See the link below for examples of that.
BackgroundWorker Class
You need to implement the BackgroundWorker.ProgressC
BackgroundWorker Class
ASKER
Hi Chris..
OK let me see if I understand what you are saying... at the moment I am using ReportProgress() and ProgressChanged() to update a progress bar following examples found on the internet. I update a ProgressBar at the end of a Very long For Loop. (see code snippets).
So If I understand you correctly you are saying that there is a way to pass some additional arguments to ReportProgress() and I can update Controls on the UI much like the ProgressBar is being updated now.. If this is true then I guess I need to figure out how to do that...
I guess I am hearing you say that I can call ReportProgress() at any time and that I can have as much code in the ProgressChanged() sub routine as I need to accomplish the Given task.... ??? True??
Question?? Can I pass a Structure to ReportProgress() that contains many Items that I need/want to change/update??
How do I do that??
Thanks in advance
Rick
OK let me see if I understand what you are saying... at the moment I am using ReportProgress() and ProgressChanged() to update a progress bar following examples found on the internet. I update a ProgressBar at the end of a Very long For Loop. (see code snippets).
So If I understand you correctly you are saying that there is a way to pass some additional arguments to ReportProgress() and I can update Controls on the UI much like the ProgressBar is being updated now.. If this is true then I guess I need to figure out how to do that...
I guess I am hearing you say that I can call ReportProgress() at any time and that I can have as much code in the ProgressChanged() sub routine as I need to accomplish the Given task.... ??? True??
Question?? Can I pass a Structure to ReportProgress() that contains many Items that I need/want to change/update??
How do I do that??
Thanks in advance
Rick
'check to see if this Process has been canceled
If BackgroundWorker1.CancellationPending Then
BackgroundWorker1.ReportProgress(0)
e.Result = intI
e.Cancel = True
Else
BackgroundWorker1.ReportProgress(intI)
End If
'EbayCDListingForm.ProgressBar3.Value = intI
Public Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ProgressBar3.Value = e.ProgressPercentage
lblPercentProgress.Text = e.ProgressPercentage.ToString() + "%"
End Sub
ASKER
Hi All..
ok I did a little test and I do see that I can indeed update some controls from within the BackgroundWorker1_Progress Changed() function, however I am not sure how to pass the information that I need to that function. Chris you mentioned creating an object to contain the data that I need to update but I guess I must honestly say that I am not sure how to go about doing that...
The BackgroundWorker1.ReportPr ogress() function appears to accept a single argument and that is an Integer.... maybe you can help me understand how to implement that feature...
BTW here is that little piece of test code that I mentioned.. it worked but of course it did not output the data that I tried to pass to it..
and yes it is a meaningless piece of code..
Oh BTW... If I can just a STRING value passed to BackgroundWorker1_Progress Changed() then I think that I can put some code together that will take care of my needs..
Thanks
ok I did a little test and I do see that I can indeed update some controls from within the BackgroundWorker1_Progress
The BackgroundWorker1.ReportPr
BTW here is that little piece of test code that I mentioned.. it worked but of course it did not output the data that I tried to pass to it..
and yes it is a meaningless piece of code..
Oh BTW... If I can just a STRING value passed to BackgroundWorker1_Progress
Thanks
Public Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim SenderValue As String
Dim SenderValueInt As Integer
SenderValue = sender.ToString
txtGetOrdersProcessInfo.Text = txtGetOrdersProcessInfo.Text + sender.ToString
'ProgressBar3.Maximum = SenderValueInt
ProgressBar3.Value = e.ProgressPercentage
lblPercentProgress.Text = e.ProgressPercentage.ToString() + "%"
End Sub
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hey Chris...
Thanks a bunch ... I did not know about the over loads... that is exactly what I needed... Let me put this all together now and I'll post the code showing how I used your suggestion/answer...
Don't sweat the syntax ... and yes it is all pretty much the same now so it's ALL Good ... hehe
Thanks Chris I'll post again in a few hours with some results
Thanks a bunch ... I did not know about the over loads... that is exactly what I needed... Let me put this all together now and I'll post the code showing how I used your suggestion/answer...
Don't sweat the syntax ... and yes it is all pretty much the same now so it's ALL Good ... hehe
Thanks Chris I'll post again in a few hours with some results
Also if I try to use the 'INVOKE' method I cannot find the 'InvokeRequired' method which I believe is the reason why my DELEGATE is only partially successful.
You can Invoke() against any control, but the FORM is most convenient.
With that in mind, you can Invoke() from within your DoWork() handler to update the UI. Be careful, however, that you only Invoke() small pieces of code that directly relate to updating the UI. Do NOT put your entire DoWork() handler inside an Invoke() call since that will nullify the benefits of running on a different thread.
Simple example:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim recordNumber As Integer
For i As Integer = 1 To 100
System.Threading.Thread.Sleep(100) ' <-- simulated "work"
recordNumber = i ' local variable used in anonymous sub below
Me.Invoke(Sub()
Label1.Text = "Processing Record #" & recordNumber
End Sub)
Next
End Sub
End Class
There's nothing wrong with using the ProgressChanged() event as suggested by the others. This is a matter of preference and code style.
This approach ~can~ be easier to understand since the code to update the UI is in the same method as our work in the DoWork() handler.
ASKER
Hi Chris,
Well that worked just fine. Thank you very much... as promised here is the code showing what I did. I simply created a sting containing the name of the control that I needed to update separated with a Pipe (|) delimiter and then the data... I then Parsed (Split) the string and used it in a Select-Case statement.. Works just fine..
Rick
Well that worked just fine. Thank you very much... as promised here is the code showing what I did. I simply created a sting containing the name of the control that I needed to update separated with a Pipe (|) delimiter and then the data... I then Parsed (Split) the string and used it in a Select-Case statement.. Works just fine..
Rick
ThisReviseListingString = "Revise" + vbTab + ItemIDArray(ArrayIndex) + vbTab + NextReviseSku + vbTab + "2.11" + vbTab + "1" + vbNewLine
objWriter.Write(ThisReviseListingString)
' Now Ceate the Revise Listing Record
BackGroundControlString = ""
BackGroundControlString = "txtGetOrdersProcessInfo" + "|" + ThisReviseListingString.ToString
BackgroundWorker1.ReportProgress(intI, BackGroundControlString)
Public Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim UserStateString As String
Dim UserStateArray() As String
UserStateString = e.UserState
UserStateArray = UserStateString.Split("|")
Select Case UserStateArray(0)
Case "txtGetOrdersProcessInfo"
txtGetOrdersProcessInfo.Text = txtGetOrdersProcessInfo.Text + UserStateArray(1).ToString
txtGetOrdersProcessInfo.Refresh()
Case "ProgressBar3.Maximum"
lblMaxNumOrders.Text = UserStateArray(1)
ProgressBar3.Maximum = CInt(UserStateArray(1))
Case "ProgressBar3.Value"
ProgressBar3.Value = CInt(UserStateArray(1))
Case "txtStandardListingFileName"
txtStandardListingFileName.Text = UserStateArray(1).ToString
Case "lblNumOrdersProcessed"
lblNumOrdersProcessed.Text = UserStateArray(1)
End Select
lblPercentProgress.Text = e.ProgressPercentage.ToString() + "%"
End Sub
ASKER
Hi Mike...
Thanks for the response It is very much appreciated...
I was busy posting my answer to Chris when you Posted so I did not see your post until later. Your suggestion is good but I have a Very large piece of code that I needed to run in the background I am not sure that it would benefit much from your suggestion but quit frankly I have NO idea if it Would or Would Not..
I have a working solution in place now and it seems to work fine... I am accepting Chris's solution but I will keep your idea in mind if things don't work well after time..
Thanks
Rick
Thanks for the response It is very much appreciated...
I was busy posting my answer to Chris when you Posted so I did not see your post until later. Your suggestion is good but I have a Very large piece of code that I needed to run in the background I am not sure that it would benefit much from your suggestion but quit frankly I have NO idea if it Would or Would Not..
I have a working solution in place now and it seems to work fine... I am accepting Chris's solution but I will keep your idea in mind if things don't work well after time..
Thanks
Rick
ASKER
Thanks to All that assisted in this solution. I very much appreciate your time and your efforts..
Rick
Rick
Glad you got it working Rick :)
No problem. Good luck!