我有这个通用函数来调用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());
因为我们已经指定this
是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());
现在我觉得即便如此,还需要打字。如果我指定this
是button1
,那么在lambda表达式中我不想再次指定虚拟变量c
来告诉操作的位置。无论如何我可以再缩短一下吗?也许就像
button1.Invoke(Hide);
或
button1.Hide.Invoke();
C#中的左右?
答案 0 :(得分: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
。
致电:
button1.Invoke(x => x.Hide());
答案 1 :(得分: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);
}
很抱歉得到长时间的回答,希望有所帮助。
答案 2 :(得分:2)
您可以使用SynchronizationContext.Post或SynchronizationContext.Send让框架将操作封送到UI线程,无论是Windows窗体还是WPF。静态SynchronizationContext.Current方法将为您的应用程序类型返回适当的同步上下文。
Post发送阻止时异步执行,直到操作完成。
以下代码将异步隐藏按钮:
SynchronizationContext.Current.Post(_=>button1.Hide(),null);
答案 3 :(得分: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的参数)。我相信这是优雅的语法。
答案 4 :(得分:1)
由于C#语法限制,它绝对不能像button1.Invoke(Hide);
或button1.Hide.Invoke();
那样完成。
但是如果你愿意放弃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;
}