WinForms classic: how to call Control.Invoke with an anonymous method delegate?
Posted: (EET/GMT+2)
Merry Christmas to everybody! Windows Forms developers might be "out" according to some .NET developers, but rest assured, the trusted technology is easy to master. Still, there are certain points where newcoming developers seem to struggle. One of them is the process of creating multi-threaded applications and updating the user interface from a thread.
Yes, this is an age-old problem, but still when trying to point developers asking the question to good and easy resource with a clean code sample, I only find solutions that are either complex or not up-to-date with current C#.
Firstly, let's assume your WinForms application has a second thread, from which you want to update a control on the user interface. Say, a log memo or a progress bar, for instance. If you try to say for example:
queryLogTextBox.Text = DateTime.Now.ToString() + ": " + message + ".\r\n" + queryLogTextBox.Text;
...in a thread, you will get an InvalidOperationException with the message "Cross-thread operation not valid: Control 'queryLogTextBox' accessed from a thread other than the thread it was created on."
Okay, good. So you know about Control.Invoke, and you know that it takes a delegate. Given this knowledge, you might be temped to think that you could simply use an anonymous method:
this.Invoke(delegate()
{
queryLogTextBox.Text = DateTime.Now.ToString() +
": " + message + ".\r\n" + queryLogTextBox.Text;
});
Unfortunately, this doesn't work in C#, because the method excepts a delegate type, not directly an anonymous method (compare this to for example Thread class' constructor). If you try this, you will get an compiler error with the message "Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type."
Okay, fair enough. But what then would be the easiest solution to this, when still using anonymous methods?
Enter the Action type, which is a delegate type for a method that doesn't return anything (void). By passing in a new Action object to Control.Invoke, the compiler is happy again. For example like this:
this.Invoke(new Action(() => queryLogTextBox.Text = DateTime.Now.ToString() + ": " + message + ".\r\n" + queryLogTextBox.Text));
Hope this helps!