在用.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的。

当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它,此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,这个做法保证了控件的安全,你可以这样理解,有人想找你借钱,他可以直接在你的钱包中拿,这样太不安全,因此必须让别人先要告诉你,你再从自己的钱包把钱拿出来借给别人,这样就安全了

another:

在设计中为了让界面与逻辑分离,我的做法是使用事件,界面只要响应事件来处理界面的显示就行了。而事件在逻辑处理中可能由不同的线程引发,这些事件的响应方法在修改界面中的控件内容时便会引发一个异常。

这时就用到了Control.InvokeRequired 属性 与Invoke方法。

MSDN中说:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。 
如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性 。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。

下面来说下这个的用法(我的一般做法):
首先定义一个委托,与这个事件处理函数的签名一样委托,当然直接使用该事件的委托也是可以的,如:

 private delegate void InvokeCallback( string msg);

然后就是判断这个属性的值来决定是否要调用Invoke函数:

 void m_comm_MessageEvent( string msg)
 {
 if (txtMessage.InvokeRequired)
 {
 InvokeCallbackmsgCallback = new InvokeCallback(m_comm_MessageEvent);
 txtMessage.Invoke(msgCallback, new object [] { msg } );
 } 
 else 
 {
 txtMessage.Text = msg;
 } 
 }

说明:这个函数就是事件处理函数,txtMessage是一个文本框。
这样就做到了窗体中控件的线程安全性。

------------------

InvokeRequired 当前线程不是创建控件的线程时为true
比如你可以自己开一个Thread,或使用Timer的事件来访问窗体上的控件的时候,在线程中窗体的这个属性就是True的。

简单的说,如果有两个线程,Thread A和Thread B,并且有一个Control c,是在Thread A里面new的。
那么在Thread A里面运行的任何方法调用c.InvokeRequired都会返回false。
相反,如果在Thread B里面运行的任何方法调用c.InvokeRequired都会返回true。
是否是UI线程与结果无关。(通常Control所在的线程是UI线程,但是可以有例外)

也可以认为,在new Control()的时候,control用一个变量记录下了当前线程,在调用InvokeRequired时,返回当前线程是否不等于new的时候记录下来的那个线程。

--------------------

我理解:如果InvokeRequired==true表示其它线程需要访问控件,那么调用invoke来转给控件owner处理。

转载于:https://www.cnblogs.com/vaevvaev/p/6909042.html

C#中Invoke的用法相关推荐

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

    转载▼ 转自:http://blog.3snews.net/html/30/34530-27563.html 在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是 ...

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

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

  3. C#中Invoke的用法()

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

  4. .Net基础——程序集与CIL HttpClient封装方法 .Net Core 编码规范 C#中invoke和beginInvoke的使用 WebServeice 动态代理类...

    .Net基础--程序集与CIL 1. 程序集和CIL: 程序集是由.NET语言的编译器接受源代码文件产生的输出文件,通常分为 exe和dll两类,其中exe包含Main入口方法可以双击执行,dll则需 ...

  5. mysql中change用法,mysql 中alter的用法以及一些步骤

    mysql 中alter的用法以及一些方法 在用到alter的时间,查到了这个文章,就copy下来了,详细进入下面链接: MySQL之alter语句用法总结 1:删除列 ALTER TABLE [表名 ...

  6. mysql中正则表达式的用法_Mysql中正则表达式Regexp常见用法

    Mysql中Regexp常见用法 模糊匹配,包含特定字符串 # 查找content字段中包含"车友俱乐部"的记录 select * from club_content where ...

  7. 【C++】C/C++ 中 static 的用法全局变量与局部变量

    C/C++ 中 static 的用法全局变量与局部变量 目录 1. 什么是static? 1.1 static 的引入 1.2 静态数据的存储 2. 在 C/C++ 中static的作用 2.1 总的 ...

  8. php foreach嵌套foreach,php中foreach怎么嵌套foreach PHP中foreach函数用法?

    foreach的使用方法小编不是很明确,分享达人指教一下.foreach (array_expressforeach($array as $key) { if(xxxx) { break; //bre ...

  9. python中str的index什么意思_python中index的用法是什么

    index() 一般用处是在序列中检索参数并返回第一次出现的索引,没找到就会报错,比如:>>> t=tuple('Allen') >>> t ('A', 'l', ...

最新文章

  1. leetcode 201. Bitwise AND of Numbers Range(位运算,dp)
  2. 数据结构Java06【赫夫曼树、概述、原理分析、代码实现(数据压缩、创建编码表、解码、压缩文件、解压文件)】
  3. 纯c语言实现的改进暗通道去雾算法测试程序(附赠大量测试图像),基于改进暗通道先验算法的图像去雾...
  4. pkill mysql_每天一个linux命令:kill命令
  5. java bitset_Java BitSet hashCode()方法及示例
  6. 如何在VB6.0里动态使用具有事件的对象
  7. 机器学习经典书籍论文
  8. 来!Flink Forward 2020 旧金山站还可以这样看
  9. 《酒吧圣经》学习笔记1
  10. linux拷贝文件和目录命令,Linux 命令 - cp: 拷贝文件和目录
  11. win7高级搜索计算机,使用win7系统高级搜索功能的方法介绍
  12. F. Clear the String (区间DP)
  13. linux公社_如何在Linux中安装和使用dig和nslookup命令
  14. Unity5.联机笔记
  15. mysql主从和redis主从
  16. 贝叶斯优化的三种实现(bayes_opt|hyperopt|optuna)
  17. 值得反复观看的几部电影
  18. 第三届互联网CIO-CTO班招募,CSDN 5个推荐名额,火热报名中
  19. html404页面怎么添加,网站要如何设置自定义404页面?
  20. SAE1939-1 用于公路设备的控制及通信网络推荐操作规程

热门文章

  1. 理解并解决IE的内存泄漏方式[翻译2]
  2. python的控件text的文本属性_只需6行Python代码就给图片加上水印——你一看就会了...
  3. 人工智能也能写出如此诗句
  4. 变焦即可判断物体的距离
  5. Storm入门(九)Storm常见模式之流聚合
  6. 罗辑思维在全链路压测方面的实践和工作笔记
  7. Python学习笔记(随笔)
  8. Eclipse 设置
  9. Ubuntu 使用phpmyadmin,报错#1146 - Table ‘phpmyadmin.pma_table_uiprefs' doesn't exist
  10. LVS的DR模型的实现