tags:

views:

473

answers:

3

Have I missed something with WPF (and Windows Forms before it) in regards to the need to write a lot of repetitive code when developing multi-threaded applications. Every UI control in the application ends up needing extra lines of code to get and set each property.

internal delegate void SetElementIsEnabledDelegate(UIElement element, bool isEnabled);
internal delegate void SetBrushesAndTextDelegate(Brush foregroundBrush, string message);
internal delegate void SetCheckBoxCheckedDelegate(CheckBox checkbox, bool checkedValue);
internal delegate void SetTextBoxTextDelegate(TextBox richTextBox, string text);
internal delegate void SetRichTextBoxTextDelegate(RichTextBox textBox, string text);

internal static void SetElementIsEnabled(UIElement element, bool isEnabled)
{
    if (element.Dispatcher.Thread != Thread.CurrentThread)
    {
        // Execute the same method, but this time on the GUI thread
       element.Dispatcher.Invoke(DispatcherPriority.Normal, new SetElementIsEnabledDelegate(SetElementIsEnabled), element, isEnabled);
       return;
   }

        element.IsEnabled = isEnabled;
}

Add another 30 delegates and respecitve methods to that.

As far as I'm aware there is no type safe way around the need to do all the thread invoking. It also seems like this is something that could have easily been taken care of under the hood in the control libraries. Am I doing something wrong and if not why would Microsoft choose not to take care of the thread invoking in the controls?

+2  A: 

Simply put, for performance reasons. Why should every GUI app have to take on the performance implications of marshalling code when only those that are multi-threaded require it?

Further, you can design your app to have less cross thread chatter, so that less invoking is necessary.

Also, your code is not particularly efficient. a better approach is:

if ( InvokeRequired ) {
    Invoke(new MethodInvoker(this.Function));
} else {
    // do code
}

EDIT: I guess my code is more for Windows Forms while yours is for WPF, but the purpose is the same. Windows requires that all control updates occur on the thread that created the control due to thread local storage issues. And this is just not something that should be there for all uses.

Mystere Man
From the performance point of view there is no marshalling required if the UI control is on the same thread.
sipwiz
But the check for marshalling is required.
Mystere Man
I imagine the overhead in the check is tiny. It's probably as simple as an if statement comparing two thread ids.
sipwiz
+3  A: 

Well you can certainly make it simpler than that. For one thing you don't need all your own separate delegate types - you can use the built-in Func<...> and Action<...> delegates if you're using .NET 3.5, and if you're not you can declare them yourself and then use them generically.

The other thing you might consider is writing a helper method (as an extension method, if you're using C# 3) and then use anonymous methods (or lambda expressions):

internal static void SetElementIsEnabled(UIElement element, bool isEnabled)
{
    InvokeIfNecessary(element, delegate
    {
        element.IsEnabled = isEnabled;
    });
}

The helper method would be:

public static void InvokeIfNecessary(UIElement element, MethodInvoker action)
{
    if (element.Dispatcher.Thread != Thread.CurrentThread)
    {
        element.Dispatcher.Invoke(DispatcherPriority.Normal, action);
    }
    else
    {
        action();
    }
}

(You should probably also include a BeginInvoke equivalent. I generally prefer BeginInvoke unless I need the rendezvous.)

Jon Skeet
A: 

Don't forget lambdas:

int myParam = 5;

Dispatcher.BeginInvoke((Action)(() => SomeMethod(myParam)));

There is also a hidden intellisense method on the dispatcher:

if(Dispatcher.CheckAccess()) DoSomething();