The problem
If you’re working on WinForms, you must know that you cannot call controls methods within a thread that is not the one that created those controls. To solve this problem, Microsoft recommend us to use the following code :
namespace TestWinForm
{
public partial class MainForm : BaseForm
{
public MainForm()
{
InitializeComponent();
}
// a delegate that has been created specially for this method
private delegate void DisplayDelegate(string text);
// a method that may be called from a worker thread
public virtual void Display(string text)
{
if (InvokeRequired)
{
Invoke(new DisplayDelegate(Display));
}
else
{
//the actual job is here
textBox1.AppendText(text);
}
}
}
}
This code is ugly because :
- you need to create a delegate for each single public method you can call from outside
- you need to put an “if/else” block in each method wich increase the complexity of your code
The trick
Here is a trick that could significantly reduce the amount of code needed to do the same job within a large project.
Within you base class
You probably have a common base class for all your UI components. Add this method :
namespace TestWinForm
{
public class BaseForm : Form
{
// This allows a sub class to easily run a method within
// an UI thread without the need of creating multiple
// delegate signatures for each method signatures
protected virtual void ThreadSafe(MethodInvoker method)
{
if (InvokeRequired)
Invoke(method);
else
method();
}
}
}
Within your UI classes
Now the only thing you need to do is to encapsulate the methode content with the ThreadSafe() method :
namespace TestWinForm
{
public partial class MainForm : BaseForm
{
public MainForm()
{
InitializeComponent();
}
// public method that may be called from outside and within any
// worker thread ...
public virtual void Display(string text)
{
ThreadSafe(delegate
{
//do any UI related code here
//note that because this is an anonym method,
//you can use the local parameter
textBox1.AppendText(text);
});
}
Happy coding !