在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往会想当然地这么写:

void ButtonOnClick(object sender,EventArgs e)

{

button.Text="关闭";

}

这样的写法运行程序之后,可能会触发异常,异常信息大致是“不能从不是创建该控件的线程调用它”。注意这里是“可能”,并不一定会触发该种异常。造成这种异常的原因在于,控件是在主线程中创建的(比如this.Controls.Add(...);),进入控件的事件响应函数时,是在控件所在的线程,并不是主线程。在控件的事件响应函数中改变控件的状态,可能与主线程发生线程冲突。如果主线程正在重绘控件外观,此时在别的线程改变控件外观,就会造成画面混乱。不过这样的情况并不总会发生,如果主线程此时在重绘别的控件,就可能逃过一劫,这样的写法可以正常通过,没有触发异常。

正确的写法是在控件响应函数中调用控件的Invoke方法(其实如果大家以前用过C++ Builder的话,也会找到类似Invoke那样的激活到主线程的函数)。Invoke方法会顺着控件树向上搜索,直到找到创建控件的那个线程(通常是主线程),然后进入那个线程改变控件的外观,确保不发生线程冲突。正确写法的示例如下:

void ButtonOnClick(object sender,EventArgs e)

{

button.Invoke(new EventHandler(delegate

{

button.Text="关闭";

}));

}

Invoke方法需要创建一个委托。你可以事先写好函数和与之对应的委托。不过,若想直观地在Invoke方法调用的时候就看到具体的函数,而不是到别处搜寻的话,上面的示例代码是不错的选择。

这样的写法有一个烦人的地方:对不同的控件写法不同。对于TextBox,要TextBoxObject.Invoke,对于Label,又要LabelObject.Invoke。有没有统一一点的写法呢?

主窗口类本身也有Invoke方法。如果你不想对不同的控件写法不一样,可以全部用this.Invoke:

void ButtonOnClick(object sender,EventArgs e)

{

this.Invoke(new EventHandler(delegate

{

button.Text="关闭";

}));

}

在C# 3.0及以后的版本中有了Lamda表达式,像上面这种匿名委托有了更简洁的写法。.NET Framework 3.5及以后版本更能用Action封装方法。例如以下写法可以看上去非常简洁:

void ButtonOnClick(object sender,EventArgs e)

{

this.Invoke(new Action(()=>

{

button.Text="关闭";

}));

}

以上写法往往充斥着WinForm构建的程序。

在微软新一代的界面开发技术WPF中,由于界面呈现和业务逻辑原生态地分开在两个线程中,所以控件的事件响应函数就不必Invoke了。但是,如果手动开辟一个新线程,那么在这个新线程中改变控件的外观,则还是要Invoke的。

C#中Invoke的用法的更多相关文章

  1. (转)C#中Invoke的用法 一

    在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...

  2. (@WhiteTaken)Unity中Invoke的用法

    今天无意间读到大神写的代码,看到了Invoke函数,于是产生兴趣.后来才明白自己要学习的东西还有很多. 下面讲用法. Invoke是延时调用函数,在用Invoke函数之前需要引入命名空间using U ...

  3. C#中Invoke的用法2

    在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...

  4. C#中Invoke的用法(转)

    在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界 ...

  5. C#中Invoke的用法()

    invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和beg ...

  6. 【转】C#中Invoke的用法

    在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界 ...

  7. C#中Invoke的用法1

    invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解.  首先说下,invoke和be ...

  8. [转载]C#中Invoke的用法()

    invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和beg ...

  9. C#中Invoke的用法()-解决子线程访问主线程控件、线程安全等问题

    引自https://www.cnblogs.com/lsgsanxiao/p/5523282.html invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概 ...

随机推荐

  1. 使用Unity创建塔防游戏(Part2)

    How to Create a Tower Defense Game in Unity – Part 2 原文地址:https://www.raywenderlich.com/107529/unity ...

  2. Docker集群实验环境布署--swarm【5 容器启动组件--node】

    10.40.100.143 docker-node0.venic.com 10.40.100.144 docker-node1.venic.com     原用swarm镜像   直接启动     1 ...

  3. 明天opp¥this xuexi 资料在高中一班

    明天opp¥this xuexi 资料在高中一班

  4. Linux 编译安装 源代码

    编译安装 源代码包的安装一般为下载软件源代码,然后编译安装. 常见的C程序软件的安装步骤是 configure, make, make install三部曲,大致是下面这样操作: 首先得安装gcc.m ...

  5. HashMap和Hashtable的同和不同

    一.综述 可以直接根据hashcode值判断两个对象是否相等吗?肯定是不可以的,因为不同的对象可能会生成相同的hashcode值.虽然不能根据hashcode值判断两个对象是否相等,但是可以直接根据h ...

  6. Hive插数据报错

    报错信息: Failed with exception MetaException(message:javax.jdo.JDODataStoreException: Put request faile ...

  7. android 控件ui

    公共参数: android:id="@+id/text_view"  给当前控件定义了 一个唯一标识符如:text_view android:layout_width=" ...

  8. 解决Intellij Idea里tomcat启动报The JRE_HOME environment variable is not defined correctly的错误

    创建一个maven项目后想启动tomcat测试一下新建的maven项目,结果一直报The JRE_HOME environment variable is not defined correctly, ...

  9. Hibernate——脏检查和缓存清理机制

    Session到底是如何进行脏检查的呢? 当一个Customer对象被加入到Session缓存中时,Session会为Customer对象的值类型的属性复制一份快照.当Session清理缓存时,会先进 ...

  10. 职责链模式(Chain of Responsibility)(对象行为型)

    1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决,不能解决就推卸给另外个一个部门(对象).至于到底谁来解决这个问题呢?政府部门就是为了可以避免屁民的请求与 ...