3

我有这个通用函数来调用 WinForm 控件:

public static void Invoke(this Control c, Action action)
{
    if (c.InvokeRequired)
        c.TopLevelControl.Invoke(action);
    else
        action();
}

我正在考虑通过引入更严格的约束来防止无意义的事情变得更好,可能就像:

button1.Invoke(() => list.Add(1));

也可能有多余的打字,如:

button1.Invoke(() => button1.Hide());

因为我们已经指定了thisis button1

所以我做到了:

public static void Invoke<T>(this T c, Action<T> action) where T : Control
{
    if (c.InvokeRequired)
        c.TopLevelControl.Invoke(action);
    else
        action(c);
}

现在我得打电话了,

button1.Invoke((c) => c.Hide());

或者

button1.Invoke((c) => button1.Hide());

现在我有点觉得即使这样也有一些超出要求的打字。如果我指定thisis button1,那么在 lambda 表达式中我不想c再次指定一个虚拟变量来告诉在哪里进行操作。无论如何我可以再次缩短这个时间吗?也许喜欢

button1.Invoke(Hide);

或者

button1.Hide.Invoke();

或者在 C# 中?

4

5 回答 5

3

为了建立其他答案,我会将其放入一个单独的扩展类中。

public static void Invoke<T>(this T c, Action<T> action) where T : Control
    {
        if (c.InvokeRequired)
            c.Invoke(new Action<T, Action<T>>(Invoke), new object[] { c, action });
        else
            action(c);
    }

这将防止TargetParameterCountException跨线程时抛出 a 。

致电:

button1.Invoke(x => x.Hide());
于 2015-01-22T19:16:04.757 回答
2

首先让我说你可能想多了 - 短代码是一件很棒的事情,但是对于任何试图阅读代码的人来说,它开始变得混乱。

现在,您的第一个建议:

button1.Invoke(Hide);

可以工作,如果你做到了:

button1.Invoke(button1.Hide); 

因为否则编译器无法知道在哪里寻找方法 Hide()。它甚至可能导致一些奇怪的行为,例如,如果所有这些代码都在某个派生类中,如下所示:

class A : Control {
    public A() {
         Button button1=new Button();
         button1.Invoke(Hide);
    }
}

现在它可以编译了,但是 Hide() 方法将是整个控件的 Hide() 方法,而不是按钮!实现这一目标的方法很简单:

public static void Invoke(this Control c, Action action) {
    c.Invoke(action);
}

后一种方式:

button1.Hide().Invoke();

即使不添加扩展方法也可以工作,你只需要做到:

((Action)button1.Hide).Invoke();

这当然意味着 Hide() 方法在当前线程中被调用,这可能不是你想要的。所以让它:

((Action)button1.Hide).Invoke(button1);
public static void Invoke(this Action action, Control c) {
    c.Invoke(action);
}

抱歉回答太长,希望对您有所帮助。

于 2012-12-24T10:41:32.920 回答
2

您可以使用SynchronizationContext.PostSynchronizationContext.Send让框架将操作封送到 UI 线程,无论是 Windows 窗体还是 WPF。静态SynchronizationContext.Current方法将为您的应用程序类型返回适当的同步上下文。

Post 异步执行,而 Send 阻塞直到操作完成。

以下代码将异步隐藏按钮:

SynchronizationContext.Current.Post(_=>button1.Hide(),null);
于 2012-12-24T11:41:22.930 回答
2

我会去:

public static void Invoke<T>(this T c, Action<T> action) where T : Control
{
    if (c.InvokeRequired)
        c.TopLevelControl.Invoke(action);
    else
        action(c);
}

button.Invoke(c => c.Hide());

它是最干净的(为您提供最初指定的按钮以执行操作)和最安全的(您不必指定button1两次...它作为 lambda 的参数返回给您) . 我相信这是优雅的语法。

于 2012-12-25T05:08:47.597 回答
1

它绝对不能像button1.Invoke(Hide);button1.Hide.Invoke();因为 C# 语法限制而完成。

但是如果你愿意放弃 IntelliSense,你可以把它缩短一点。不利的一面是,通常可以在编译时检测和修复的一些错误(如拼写错误或参数不匹配)将成为运行时错误。有时可以接受,有时不能。

展望未来,这是一个示例用法

button1.Invoke("Hide");

或者

button1.Invoke("ResumeLayout", true);

解决方案:

internal static class ExtensionMethods
{
    internal static object Invoke<TControl>(this TControl control,
        string methodName, params object[] parameters)
        where TControl : Control
    {
        object result;

        if (control == null)
            throw new ArgumentNullException("control");

        if (string.IsNullOrEmpty(methodName))
            throw new ArgumentNullException("methodName");

        if (control.InvokeRequired)
            result = control.Invoke(new MethodInvoker(() => Invoke(control,
                methodName, parameters)));
        else
        {
            MethodInfo mi = null;

            if (parameters != null && parameters.Length > 0)
            {
                Type[] types = new Type[parameters.Length];
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (parameters[i] != null)
                        types[i] = parameters[i].GetType();
                }

                mi = control.GetType().GetMethod(methodName,
                    BindingFlags.Instance | BindingFlags.Public,
                    null,  types, null);
            }
            else
                mi = control.GetType().GetMethod(methodName,
                    BindingFlags.Instance | BindingFlags.Public);

            if (mi == null)
                throw new InvalidOperationException(methodName);

            result = mi.Invoke(control, parameters);
        }

        return result;
    }
于 2012-12-24T11:08:59.493 回答