(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 re开发者_开发问答ference 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).
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.
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.
精彩评论