Friday, December 7, 2012

WPF - CheckAccess, Invoke and BeginInvoke On Dispatcher(Similar to InvokeRequired) C#

Assuming we are aware of thread safe model of updating control's text from a different thread(not the main thread), I will explain how do we do the same in WPF.

Every control has a DispatcherObject. Just like in Windows Forms only the thread that created Dispatcher can access that object. We will use either BeginInvoke or Invoke on this dispatcher object to update the text  of the control. Invoke is synchronous and hence will block the call until main thread completes this request. Be cautious while using Invoke in multithreading applications, you might get blocked on this call and you might lose the whole point of being on different thread.

BeginInvoke is asynchronous call on the method that you specify in the delegate. Both Invoke and BeginInvoke accept delegate to the method to be invoked as argument and also parameters to this method as object array. 

For identifying whether we are trying to update control's property on main thread or some other thread we used InvokeRequired in Windows Forms, in WPF we use CheckAccess() method on the dispatcher object of control. Note intellisense might not work for this method, you can go ahead and type it.

I have copied a simple example which combines concept of AutoResetEvent and also BeginInvoke and check access which avoid any "InvalidThreadException: Calling thread can not access this object because a different thread owns it"! exception.

I have used a simple delegate:
        delegate void  textupdater(string text);

private void AutoResetEvent_Click(object sender, RoutedEventArgs e)
        {
            textBox1.Text = string.Empty;
            
            Thread t = new Thread(new ParameterizedThreadStart(HelloWordlInLoop));            
            t.Start(10);
            handle.WaitOne();
            updateText("In the main thread after event handle has been reset");
        }

        void updateText(string text)
        {
            try
            {
                if (textBox1.Dispatcher.CheckAccess())
                {
                    textBox1.Text += text;
                    textBox1.Text += "\n New Update via BeginInvoke";
                }
                else
                {
                    textBox1.Dispatcher.BeginInvoke(new textupdater(updateText), DispatcherPriority.Normal,     
                                                     new object[] { text });
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        void HelloWordlInLoop(object obj)
        {
            int i = (int)obj;
            while (i-- > 0)
            {
                Thread.Sleep(1000);
                updateText("\n Updating from Second Thread without setting EventHandle. i = " + i.ToString());
            }
            handle.Set();
        }