Eugen
Eugen

Reputation: 2990

Simulate PostMessage

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

Answers (1)

Jcl
Jcl

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

Related Questions