小编典典

自动化 InvokeRequired 代码模式

all

我已经痛苦地意识到需要在事件驱动的 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()
语法既低效又 难以 处理。

那么,有没有人想出捷径呢?


阅读 76

收藏
2022-07-06

共1个答案

小编典典

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

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.


更新 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);
}
2022-07-06