我已经痛苦地意识到需要在事件驱动的 GUI 代码中编写以下代码模式的频率,其中
private void DoGUISwitch() { // cruisin for a bruisin' through exception city object1.Visible = true; object2.Visible = false; }
变成:
private void DoGUISwitch() { if (object1.InvokeRequired) { object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); })); } else { object1.Visible = true; object2.Visible = false; } }
这是 C# 中的一个尴尬模式,无论是记忆还是打字。有没有人想出某种捷径或结构来在一定程度上实现自动化?如果有一种方法可以将函数附加到执行此检查的对象,而无需完成所有这些额外的工作,例如object1.InvokeIfNecessary.visible = true类型快捷方式,那就太酷了。
object1.InvokeIfNecessary.visible = true
以前的答案已经讨论了每次只调用 Invoke() 的不切实际,即使这样 Invoke() 语法既低效又 难以 处理。
那么,有没有人想出捷径呢?
Lee的方法可以进一步简化
public static void InvokeIfRequired(this Control control, MethodInvoker action) { // See Update 2 for edits Mike de Klerk suggests to insert here. if (control.InvokeRequired) { control.Invoke(action); } else { action(); } }
并且可以这样称呼
richEditControl1.InvokeIfRequired(() => { // Do anything you want with the control here richEditControl1.RtfText = value; RtfHelpers.AddMissingStyles(richEditControl1); });
无需将控件作为参数传递给委托。C# 自动创建一个闭包。
如果你必须返回一个值,你可以使用这个实现:
private static T InvokeIfRequiredReturn<T>(this Control control, Func<T> function) { if (control.InvokeRequired) { return (T)control.Invoke(function); } else { return function(); } }
更新 :
根据其他几张海报Control可以概括为ISynchronizeInvoke:
Control
ISynchronizeInvoke
public static void InvokeIfRequired(this ISynchronizeInvoke obj, MethodInvoker action) { if (obj.InvokeRequired) { var args = new object[0]; obj.Invoke(action, args); } else { action(); } }
DonBoitnott 指出,与接口不同Control,方法ISynchronizeInvoke需要一个对象数组Invoke作为action.
Invoke
action
更新 2
Mike de Klerk 建议的编辑(请参阅插入点的第一个代码片段中的注释):
// When the form, thus the control, isn't visible yet, InvokeRequired returns false, // resulting still in a cross-thread exception. while (!control.Visible) { System.Threading.Thread.Sleep(50); }