שתף באמצעות


Cross-thread operation not valid: Control 'ProgressBar1' accessed from a thread other than the thread it was created on.

Question

Sunday, February 17, 2013 6:27 AM

Run the code from support.microsoft.com/kb/315577 

This a threading example codes.

But the following error occured.

"Cross-thread operation not valid: Control 'ProgressBar1' accessed from a thread other than the thread it was created on."

How can I solve it ?

All replies (7)

Sunday, February 17, 2013 10:32 PM ✅Answered

My answer is "yes, it's fine - you're not touching any UI objects", but most here will say "NO! - you're reaching across the threads!"

I've challenged in the past to show me where this is a violation - I've looked and I can't find anything. Specifically I reference this from MSDN, which in part states as follows:

"You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events."

What you do NOT see there is any mention of what you're doing. You are NOT manipulating any UI objects, so I think it's fine and I've done it many many times with yet a hint of an issue.

I'm sure this will raise the ire of many when I post this so ... hang on, here we go for a ride! ;-)

Please call me Frank :)


Sunday, February 17, 2013 6:44 AM

The earliest releases of the .Net Framework and Visual Studio did not check for these illegal cross-thread calls.  Perhaps the example you are using was originally written for this version of the framework and has not been updated.

You can work around it (not recommended) or solve it by using the procedure described here:
http://tech.xster.net/tips/invoke-ui-changes-across-threads-on-vb-net/


Sunday, February 17, 2013 7:22 PM | 1 vote

take the same form with the button and progress bar, add a background worker, be sure to set the "Can Report Progress" to true and use this code:

Public Class Form1
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        MessageBox.Show("This is the main thread")
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim stp As Integer
        Dim newval As Integer
        Dim rnd As New Random()

        Do
            stp = ProgressBar1.Step * rnd.Next(-1, 2)
            newval = ProgressBar1.Value + stp
            If newval > ProgressBar1.Maximum Then
                newval = ProgressBar1.Maximum
            ElseIf newval < ProgressBar1.Minimum Then
                newval = ProgressBar1.Minimum
            End If
            BackgroundWorker1.ReportProgress(newval)
            System.Threading.Thread.Sleep(100)
        Loop
    End Sub
    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
    End Sub
End Class

Sunday, February 17, 2013 7:29 PM

Devon,

If that doesn't thrown a cross-thread exception then it should.

Don't "touch" the UI controls (the progressbar) inside the worker itself. I don't have a good suggestion for what you're trying to show but - what you have there isn't a good idea.

You could, though, just start a loop and each nth time, use the worker's .ReportProgress to show how it could work.

Not trying to be difficult - I hope you know that. :)

Please call me Frank :)


Sunday, February 17, 2013 9:34 PM

Not a problem Frank, I always appreciate constructive advice.

Do you mean reading the ProgressBar value is not a good idea ?

I do use the ReportProgress to update it and I always thought that was OK to do that.


Sunday, February 17, 2013 9:38 PM

Not a problem Frank, I always appreciate constructive advice.

Do you mean reading the ProgressBar value is not a good idea ?

I do use the ReportProgress to update it and I always thought that was OK to do that.

Right.

If it's in the form, don't touch it. It'll catch up to you eventually and you'll wonder what happened!

You can pass things in as parameters (e.Argument), pass them back out (e.Result) but in between, don't touch the UI things except through the .ReportProgress().

It'll catch up to you unexpectedly too (been there, done that!). ;-)

Please call me Frank :)


Sunday, February 17, 2013 10:21 PM

Can this be done, using variables?

Public Class Form1
    Dim PBCurVal As Integer
    Dim PBMaxVal As Integer
    Dim PBMinVal As Integer
    Dim PBStep As Integer
    Dim stp As Integer

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim newval As Integer
        Dim rnd As New Random()

        Do
            stp = PBStep * rnd.Next(-10, 11)
            newval = PBCurVal + stp
            If newval > PBMaxVal Then
                newval = PBMaxVal
            ElseIf newval < PBMinVal Then
                newval = PBMinVal
            End If
            BackgroundWorker1.ReportProgress(newval)
            System.Threading.Thread.Sleep(100)
        Loop
    End Sub
    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Sub Button1_Click_1(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        MessageBox.Show("This is the main thread")
    End Sub

    Private Sub Form1_Load_1(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        PBMaxVal = ProgressBar1.Maximum
        PBMinVal = ProgressBar1.Minimum
        PBStep = ProgressBar1.Step
        PBCurVal = ProgressBar1.Value
        BackgroundWorker1.RunWorkerAsync()
    End Sub
End Class