views:

72

answers:

2

(There's a tiny bit of history to this question, please bear with me)

In this question, I talked about the possibility of centralising the 'cross thread' hocus-pocus that is required for updating the GUI like so:

//Utility to avoid boiler-plate InvokeRequired code
//Usage: SafeInvoker.Invoke(myCtrl, () => myCtrl.Enabled = false);
public static void Invoke(Control ctrl, Action cmd)
{
    if (ctrl.InvokeRequired)
        ctrl.BeginInvoke(new MethodInvoker(cmd));
    else
        cmd();
}

Last week, mulling over the fact that this always happens (in my code) when handling an event, and partly inspired by Dustin Campbell's event extension method, I cooked up this:

//Utility to avoid boiler-plate SafeInvoker.Invoke code
//Usage obj.EventRaised += obj_EventRaised.OnGUIThread(controlreference);
public static EventHandler OnGUIThread(this EventHandler h, Control ctrl)
{
    // lambda expressions are not syntactic sugar, they are syntactic crack!
    return (s, e) => SafeInvoker.Invoke(ctrl, () => h(s, e));
}

The thing that bugs me here is always having to have a control to hand. As far as I know, there is only one GUI thread, so any control would do here.

I got to wondering about creating a 'GUIContext' singleton and throwing it a reference to my main form when the application starts up, then accessing that from my extension method, thus removing the need for the ctrl parameter.

Is this a bad idea, and if so, why? Is there a better way to do it? I know that in Rx there is a notion of Context, but I'm not aware of anything equivalent in vanilla WinForms. I can imagine there might be a problem if I try to update a control that is not yet handled (but in that case I'm screwed anyway).

+1  A: 

I suspect that in many cases the SynchronizationContext.Current may do much of what you want here (watch out - it can also be null). But just Send or Post to that.

If you do keep a global object - perhaps type it just as ISynchronizeInvoke - less chance of unintended abuse.

Marc Gravell
Any pointers for groking SynchronizationContext? Have I correctly understood that using this also implies modifying the code that raises the event? (Not an option for me in this instance).
Benjol
And I guess it doesn't need to be a globally *accessible* object, I'd only be using it from within the Extension class.
Benjol
@Benjol - it depends on how the code (and thread) that raises the event came to be... the context needs to be set.
Marc Gravell
+1  A: 

Doing this would restrict you to a single main form and a single GUI thread. But the need for a main GUI thread is as much a requirement of .NET forms (and the underlying Win32 API) as anything, so it's not very likely to change.

You would know if having A single main form for your app is likely to change or not. Even if it it did, your singleton would be a better place to keep track of which form was "main" than to pass that around to all of the background threads.

On the whole, this looks like a reasonable design to me. I've used a global variable to hold hwndMain in my unmanaged apps for more than a decade and never regretted it.

John Knoeller