הערה
הגישה לדף זה מחייבת הרשאה. באפשרותך לנסות להיכנס או לשנות מדריכי כתובות.
הגישה לדף זה מחייבת הרשאה. באפשרותך לנסות לשנות מדריכי כתובות.
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