Some Creativity

Weblog of Siddharth Uppal

Archive for January 10th, 2008

WPF equivalent of InvokeRequired

with one comment

If you’re reading this article, you’re most probably familiar with the InvokeRequired property on Windows.Forms.Control class which we have to use whenever setting a property on a Windows Forms control from a thread other than the one it was created on. Here is what the MSDN page about InvokeRequired says:

Controls in Windows Forms are bound to a specific thread and are not thread safe. Therefore, if you are calling a control’s method from a different thread, you must use one of the control’s invoke methods to marshal the call to the proper thread. This property can be used to determine if you must call an invoke method, which can be useful if you do not know what thread owns a control. There are four methods on a control that are safe to call from any thread: Invoke, BeginInvoke, EndInvoke and CreateGraphics. For all other method calls, you should use one of these invoke methods when calling from a different thread.

Is a similar thing needed in WPF?

I wrote a simple WPF application to test it out. The application just contains a textbox, and two buttons inside a StackPanel.


        <textbox Name="txtMain">
        </textbox>
        <button name="btnChangeText" Click="btnChangeText_Click" >
            Change Text Async
        </button>
        <button name="btnChangeTextProperly">
            Change Text Async Properly
        </button>

As you can see in the markup, the Click event on the first button is bound to a btnChangeText_Click event-handler. The second button doesn’t do anything right now (by the text on the button you have probably guessed that a twist is coming up in the story line soon).

image001.png

Let’s look at the first button’s Click event handler:


        private void btnChangeText_Click(object sender, RoutedEventArgs e)
        {
            this.mChanger = new System.Threading.Thread(new System.Threading.ThreadStart(this.ChangeText));
            this.mChanger.Start();
        }

The handler just starts a thread and asks the Thread to call the ChangeText method. Here is what the ChangeText method looks like:


        private void ChangeText()
        {
            try
            {
                this.txtMain.Text = "Hello World!";
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), ex.Message);
            }
        }

At runtime, here is what happens when you click on the first button.

image003.png

This is the MessageBox from the catch block in ChangeText. WPF throws an InvalidOperationException at us.

So it turns out there’s a need for a WPF equivalent of InvokeRequired property on Windows.Forms.Control class.

That’s the twist (Or is it?).

Let’s modify the XAML markup for the second button to add a Click event handler.


        <button name="btnChangeTextProperly" Click="btnChangeTextProperly_Click">
            Change Text Async Properly
        </button>

And here’s the code for the event handler. It’s similar to the previous method but we’re calling a ChangeTextProperly method this time.


        private void btnChangeTextProperly_Click(object sender, RoutedEventArgs e)
        {
            this.mChanger = new System.Threading.Thread(new System.Threading.ThreadStart(this.ChangeTextProperly));
            this.mChanger.Start();
        }

Here’s the code for ChangeTextProperly method.


        private void ChangeTextProperly()
        {
            if (this.txtMain.Dispatcher.CheckAccess())
            {
                this.txtMain.Text = "Hello World";
            }
            else
            {
                this.txtMain.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new TextChanger(this.ChangeTextProperly));
            }
        }

TextChanger is just a delegate declared as:


        private delegate void TextChanger();

All WPF controls have a Dispatcher property available on them. We need to call the CheckAccess method to check if this code is executing on the same thread as the control. If not, we create a delegate pointing to this function and ask Dispatcher to invoke the delegate on the thread that created the control.At runtime, click on the second button now and everything is peaceful again.
image006.jpg

I’ll leave you with some links:

Written by Sid

January 10th, 2008 at 12:50 pm

Posted in .NET, Tech/Hacks