一尘不染

自动执行InvokeRequired代码模式

c#

我已经痛苦地意识到,需要多长时间在事件驱动的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类型快捷方式),那就太好了。

先前的答案已经讨论了每次调用Invoke()都是不切实际的,即使这样,Invoke()语法效率低下, 仍然 难以处理。

那么,有人找到快捷方式吗?


阅读 393

收藏
2020-05-19

共1个答案

一尘不染

李的方法可以进一步简化

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#自动创建一个闭包


更新

根据其他几个海报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指出,与接口不同,ControlISynchronizeInvoke接口需要该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);
}

有关此建议的担忧,请参见下面的ToolmakerSteve的评论。

2020-05-19