开发者

Keeping a global reference to a control in WinForms for accessing GUI thread

开发者 https://www.devze.com 2022-12-20 00:07 出处:网络
(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

(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.

0

精彩评论

暂无评论...
验证码 换一张
取 消