Reputation: 211
VB.NET threading.
I tried to update the button text on a form from a backgroundworker.do_work event, and it failed, with the usual cross-thread exception message.
However, by pure chance, I also tried to update text in a system.windows.form.toolstripstatuslabel also from this backgroundworker.do_work event, and it DOES work.
Question: why is this? Is it perhaps because theres some kind of implicit shared behaviour with system.windows.form.toolstripstatuslabel?
thanks all.
Upvotes: 0
Views: 471
Reputation: 941277
It is an implementation detail for the ToolStripItem class. It acts like a Control but does not in fact derive from Control. It is a 'windowless' control, using the window of its Owner to draw itself. Changing the Text property cause the Owner's Invalidate() method to be called. Which eventually causes it OnPaint method to be called and causes the item to be drawn. On the UI thread. Invalidate() is a thread-safe method, it merely sets an internal "this window needs a Paint event" status bit.
While this does avoid the typical UI threading woes, like deadlock or outright crashes, it is not in fact completely thread-safe. You could set the Text property at the exact same time the item's paint method is running. Ending up with visible text that isn't actually updated. Very low odds, not zero.
Upvotes: 1
Reputation: 812
One important leaky abstraction to remember about Windows Forms
is that a window Handle
is not created until it is actually required. Ie. Handle won’t be created when you instantiate Form. Rather handle is created lazily
and have a real Window hwnd
only when we first get call something like Show()/ShowDialog(). Ie. If you are calling any of Show/ShowDilalog in worker thread completion then your handle is created on a worker thread. Here no one had asked for the form's Handle, ever, until it was accessed as part of the InvokeRequired
( posting action asynch) check that occurred when the worker thread had completed its work. This might be the reason you are seeing this issue once in a while.
Upvotes: 0
Reputation: 48139
How are you creating / invoking your background worker thread. You should be doing something like... (and this is C#, not VB)
// This would be a method in your form...
private void CallBackgroundWorker()
{
YourBackgroundWorkerClass BGW = new YourBackgroundWorkerClass();
BGW.WorkerReportsProgress = true;
BGW.ProgressChanged += MyFormsBGW_ProgressChanged;
BGW.RunWorkerAsync();
}
// this would be in your form too... to get feedback from worker to
// let the form's interface/control update itself
protected void MyFormsBGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.SomeButton.Text = "whatever";
}
Then, somewhere in your background worker, just issue a call to ReportProgress()... the value could be almost anything, even if representing a percentage, such as ReportProgress(100). This will force call back directly to the FORM's thread (on the UI thread), let it update what it needs to, and then return back to the background worker to continue.
Upvotes: 0
Reputation: 1038710
why is this?
It's pure chance. At times it might work at others it might crash. You should never update any GUI controls from threads on which they were not created (which is basically the main GUI thread).
Upvotes: 1