Introduction
Threads are nice when you need to do stuff in the background without causing your application to hang. Often you want to show the progress of the load that is being handled. In many occasions, that's a progressbar but in some you want to show in detail what is being done. In the first situation where it is only needed to update a progress bar, a backgroundworker can be used. The background worker sends thread safe events. In that event, you can update your progress bar. In the other situations where you want to show more information of what is being done, you need to call the form from within the other thread. If you do that, you will get an invalid crossthreadcall exception. Many articles that discuss the problem explain how to solve the problem by creating delegates and invoking them.
Example: How to: Make Thread-Safe Calls to Windows Forms Controls
delegate void SetTextCallback(string text);
private void ThreadProcSafe()
{
Thread.Sleep(2000);
string text = "Written by the background thread.";
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke
(d, new object[] { text + " (Invoke)" });
}
else
{
this.textBox1.Text = text + " (No Invoke)";
}
}
private void SetText(string text)
{
this.textBox1.Text = text;
}
I probably don't have to mention this is a lot of code for one simple textbox
update.
An easy alternative
Finally, I found a quick and elegant solution to update a form from another thread. Thanks to some great feedback in the comments i was able to futher perfect the implementation. The code is as follows:
lblProcent.SafeInvoke(d => d.Text = "Written by the background thread");
progressBar1.SafeInvoke(d => d.Value = i);
this.SafeInvoke(d => d.UpdateFormItems("test1", "test2"));
A threadSafe
getter is also available. The getter knows what the return type is, so casting is not necessary.
string textboxtext=textbox.SafeInvoke(d=>d.textbox.text);
The function SafeInvoke
are extension methods.
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
I hope this helps
History
- 17/01/2010 Article published
- 30/01/2010 Sample project added and article updated with the comments of philippe dykmans
- 8/10/2010 Sample project changed and article updated with the comments of philippe dykmans