(这个问题有一点历史,请耐心等待我)
在this问题中,我谈到了集中更新GUI所需的'跨线程'hocus-pocus的可能性,如下所示:
//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();
}
上周,在处理事件时总是发生这种情况(在我的代码中),并且部分受到Dustin Campbell事件扩展方法的启发,我对此进行了研究:
//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));
}
让我感到烦恼的是,总是需要控制。据我所知,只有一个GUI线程,所以任何控制都可以在这里完成。
我想知道创建一个'GUIContext'单例并在应用程序启动时向我的主窗体引用它,然后从我的扩展方法访问它,从而不再需要ctrl参数。
这是一个坏主意,如果是,为什么?有没有更好的方法呢?我知道在Rx中有一个Context的概念,但我不知道vanilla WinForms中的任何东西。我可以想象如果我尝试更新尚未处理的控件(但在那种情况下我是screwed anyway)可能会出现问题。
答案 0 :(得分:1)
我怀疑在很多情况下SynchronizationContext.Current
可能会在这里做很多你想做的事情(注意 - 它也可以是null
)。但只是Send
或Post
。
如果你做保留一个全局对象 - 也许就像ISynchronizeInvoke
那样输入它 - 意外滥用的可能性更小。
答案 1 :(得分:1)
执行此操作会将您限制为单个主窗体和单个GUI线程。但是对于主要GUI线程的需求与.NET表单(以及底层的Win32 API)一样需要,因此它不太可能发生变化。
您会知道您的应用的单个主要表单是否可能会更改。即使它确实如此,你的单身人士也会更好地跟踪哪个形式是“主要”而不是将其传递给所有后台线程。
总的来说,这看起来像是一个合理的设计。我已经使用全局变量将hwndMain保存在我的非托管应用程序中超过十年,并且从未后悔过。