Using BackGround Worker

What I have is an application that has 2 forms(Main, Label). What I ma doing is that on the Label form th program needs to search the database, read a label file and then print about 3 different things and the run a shell command .bat that updates another program. What I thought I could to was to use a background worker to accomplish this. My problem is when I try to access the .Text value of a contol on the label form in a case statement it gives me the error below.
"Cross-thread operation not valid: Control 'cboLabelType' accessed from a thread other than the thread it was created on." I do not need to change the value on this control I just need to be able to access it. How can this be done? I a, using VB.net
kashwmuAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

käµfm³d 👽Commented:
Break the code where you access the Text property out into its own function. Then check for the InvokeRequired property's value to see if you are calling it from a thread. If you are calling from a child thread, then you must use the Invoke method to access the control. The reason for this is that UI controls are created on the main thread of an application and .NET does not allow direct access of members of one thread to be accessed by other threads.
Private Delegate Sub GetLabelTextDelegate(ByVal text As String)

Private Function GetLabel() As String
    If Me.InvokeRequired Then
        Dim d As New GetLabelTextDelegate(AddressOf GetLabel)

        Return Me.Invoke(d)
    Else
        Return Me.Label1.Text
    End If
End Function

Open in new window

0
Mike TomlinsonMiddle School Assistant TeacherCommented:
Good answer by kaufmed.

Generally speaking, though, if the values of the Labels will not change during the operation then it's a good practice to gather all the necessary info for a thread BEFOREHAND and pass it into the thread so it has access to it locally and the Invoke() isn't needed.

You can pass things into the RunWorkerAsync() method:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync(cboLabelType.Text)
    End Sub

    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim argument As String = CType(e.Argument, String)

    End Sub

The example above is passing just one value (a string) but you can pass multiple items by either using a List or by creating a Custom class if you need multiple types of data passed in.
0
käµfm³d 👽Commented:
I always get nervous when my comments are followed by yours, Idle...   but I always end up learning something   ;)
0
The Ultimate Tool Kit for Technolgy Solution Provi

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy for valuable how-to assets including sample agreements, checklists, flowcharts, and more!

Mike TomlinsonMiddle School Assistant TeacherCommented:
Haha...I know the feeling too!  =)

Both approaches definitely have their place when used correctly.
0
APag96Commented:
control.checkforillegalthreadcalls = false

not sure if that is the exact name
0
käµfm³d 👽Commented:
As I understand it, CheckForIllegalThreadCalls would be used in RARE circumstances and only by someone who knows exactly what they are doing (i.e. the implications of taking off such a check).

While this application may be simple enough that enabling such a check would be harmless, I would advise against its use here to prevent the idea that it is OK to use this property wantonly. I would recommend getting in the habit of checking the InvokeRequired property (or passing in data, per Idle_Mind) as to prevent issues in future projects where the total effects of using CheckForIllegalThreadCalls may not be so apparent.
0
käµfm³d 👽Commented:
I should have searched it before my last post, but the property name is CheckForIllegalCrossThreadCalls   :)
0
kashwmuAuthor Commented:
So i have used the example by kaufmed and got this error
"Method 'Private Function GetLabel() As String' does not have the same signature as delegate 'Delegate Sub GetLabelTextDelegate(text As String)'." What is this trying to tell me?
0
käµfm³d 👽Commented:
The signature of a function is its argument list. As you posted, GetLabel() has no parameters whereas GetLabelTextDelegate has one--a string parameter. These two lists are not the same, hence, their signatures do not match. If GetLabel took one parameter--let's call it "value"--then if "value" were of type string, the signatures of both functions would match.

When working with delegates, in order to have a execute a delegate, the signature described in the delegate must match the signature of the function that will be executed (the function the delegate points to). Since GetLabel had no parameters and GetLabelTextDelegate describes a function that returns void and has a single string parameter, the two signatures are different and hence incompatible.

This was an error on my part. The correct code should be:
Private Delegate Function GetLabelTextDelegate() As String

Private Function GetLabel() As String
    If Me.InvokeRequired Then
        Dim d As New GetLabelTextDelegate(AddressOf GetLabel)

        Return Me.Invoke(d)
    Else
        Return Me.Label1.Text
    End If
End Function

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
käµfm³d 👽Commented:
>>  If GetLabel took one parameter--let's call it "value"--then if "value" were of type string, the signatures of both functions would match.

Something I forgot to mention:  If "value" were of some other type (say Int32), then the signatures would not match.
0
kashwmuAuthor Commented:
OK, I am going to pile on another error. I made the changes that you suggested and everything worked. The program ran and did all that it needed to until it got to the end of the BackgroudWorker sub. That is when it gave me this error: "Exception has been thrown by the target of an invocation". I have treid to catch the inner exception from almost everywhere but how do you catch an inner exception when the exception happens right at the End Sub line? Any help is appreciated.
0
käµfm³d 👽Commented:
I ran into a situation like that a while back (exception on last line of function)...  of course I can't recall what the specifics were or what the resolution was....  sorry  :(

One thing you might try, though, is to go to Debug->Exceptions and check the "Common Language Runtime Exceptions" checkbox under the "Thrown" column. This will cause the IDE to halt at the exact time the exception is thrown on any CLR exception generated. Mind you, this will also break on exceptions for which you have written handlers (try/catch). If you are still unable to see the exception, you might want to enable all of the checkboxes under the "Thrown" column. This will yield numerous breaks as .NET handles a ton of exceptions for you behind the scenes. Disregard these intermediate exceptions.
0
kashwmuAuthor Commented:
Just to wrap it up for some reason the code below was not working and giving and object was not an instance error. It did not like e.Result.toString so i change the to just a string of "Done" and it all proccessed without error. I am not real sure why that happened but since it is working I guess I am ok with it. Thanks for all the help.

Private Sub Worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)

        frmMain.lblTravss.Text = e.Result.toString()
        frmMain.pbStatus.Style = ProgressBarStyle.Blocks
        frmMain.pbStatus.Value = 0

    End Sub

Open in new window

0
kashwmuAuthor Commented:
Very good and easy solution
0
CodeCruiserCommented:
>e.Result.toString()

The error is happening because the e.Result is null. Do you set the result?
0
käµfm³d 👽Commented:
NP. Glad to help  :)


As to your last post:

The Result member of "e" is a value which you would set inside your "thread". To set it, you would use code like:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    e.Result = "Some result";
}

Open in new window

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.