Reputation: 2990
In regular WinAPI you can send a message to a window inside your application using PostMessage which would not wait for the message to be processed, it returns immediately.
Now in my window I receive this message which is called in the same UI thread where I can manipulate some other UI controls.
How can I simulate this behavior in .NET WinForms?
I know I can use Delegate.BeginInvoke which will call my method async, but it won't be executed in same UI thread, and any access to the UI controls would raise an exception. I know I can use Control.Invoke to change the UI inside that method, but that's too much to write code, I want do not worry with this and work same way as in WinAPI.
I have such a code
public delegate void DoDelegate();
private DoDelegate d;
public Form1()
{
InitializeComponent();
d = new DoDelegate(Do);
}
private void button1_Click(object sender, EventArgs e)
{
d.BeginInvoke(ar =>
{
AsyncResult result = (AsyncResult)ar;
DoDelegate dd = (DoDelegate)result.AsyncDelegate;
dd.EndInvoke(ar);
}, null);
}
public void Do()
{
Action a = () =>
{
for (int i = 0; i < 10; i++)
{
label1.Text = "Item " + i;
Thread.Sleep(1000);
}
};
if (InvokeRequired)
Invoke(a);
else a();
}
which blocks the main thread even though is called in a separate one. I know why, because I use Control.Invoke on whole method and I should use it only around setting the .Text property, but again, that's too much, is easy forget use Invoke and then all crashes.
So, basically, how do I call a method async, which won't create any issues to access UI without Invoke mechanism, same same I do using PostMessage?
Ideas?
Upvotes: 0
Views: 482
Reputation: 28272
I use this extension method:
public static void InvokeIfRequired(this Control control, Action action)
{
if (control.InvokeRequired)
control.Invoke(action);
else
action();
}
Which I use on any multithreaded code accesing the UI. In your case, it'd do like:
label1.InvokeIfRequired(() => { label1.Text = "Item " + i });
Note that if you are using it widely for just single changes to a control, it might be wise to have an overload of the extension method like:
public static void InvokeIfRequired(this Control control, Action<Control> action)
{
if (control.InvokeRequired)
control.Invoke(new Action(() => action(control)));
else
action(control);
}
So you can do:
label1.InvokeIfRequired(x => { x.Text = "Item " + i });
Not having to repeat the control on the lambda expression
Upvotes: 1