invoke
在用.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的。
invoke的更多相关文章
- P/Invoke:C#调用C++
P/Invoke的全称是Platform Invoke (平台调用) 它实际上是一种函数调用机制通 过P/Invoke我们就可以调用非托管DLL中的函数. P/Invoke依次执行以下操作: 1. 查 ...
- C# Invoke或者BeginInvoke的使用
在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...
- Invoke的使用情景
直接看代码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Dat ...
- 关于控件的Invoke(...)方法和BeginInvoke(...)方法的区别
这两个方法最主要的区别就是一个是同步,一个是异步,即会阻塞线程,那么阻塞哪个线程呢?我们用代码来分析(工具是VS2010) using System; using System.Collections ...
- 多线程、委托、Invoke解决winform界面卡死的问题,并带开关
一.知识点介绍 1,更新控件的内容,应该调用控件的Invoke方法. Invoke指: 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托.该方法接收一个委托类型和委托的参数,因此需要定义 ...
- (转)C#为什么要使用Invoke,它和BeginInvoke有什么区别
在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...
- .NET学习之路----我对P/Invoke技术的理解(一)
看了P/Invoke技术的介绍,于是想写下点东西,东西包含两个部分:知识的纪录和我的理解及疑问. r托管代码中调用非托管API函数的过程 1.定位包含API的DLL: 2.载入DLL 3.找到DLL中 ...
- Sharepoint client model 中出现Cannot invoke HTTP DAV request. There is a pending query 的解决办法
由于近期在某项目中使用sharepoint client 对象模型做项目 在sharepoint 2010环境下正常,但迁移到sharepoint 2013后报错,提示如下 Cannot invoke ...
- 关于InvokeRequired与Invoke
from:http://www.th7.cn/Program/net/201306/140033.shtml Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性.因此,如果从另一个线程调 ...
- simvision1 database和invoke
VCD是一种ASCII码的文件,可以直接用gvim来打开.有两种格式:1)Four-state, 2) Extended, 相比较而言,Extended VCD会多一些strength的信息. VC ...
随机推荐
- R语言-数据结构
1.向量 向量是用来存储数值型.字符型或逻辑性数据的一维数组,用函数c()创建向量 a <- c(1,2,5,6,4) b <- c("one","two&q ...
- 扩展ValidationAttribute 1
MVC中经常会用一些服务端对Model的验证. 服务端验证要继承自ValidationAttribute,并重写IsValid虚方法来自定义自己的验证规则. protected override Va ...
- 关于Maven项目
用maven 工程搭建项目,在搭建好之后,运行时却抛出了这样的错误: Error configuring application listener of class org.springframew ...
- 汇编语言学习与Makefile入门
继续开发 ; hello-os ; TAB= ORG 0x7c00 ; 指明程序的装载地址 ; 以下的记述用于标准FAT12格式的软盘 JMP entry DB 0x90 DB "HELLO ...
- java 事件监听 - 鼠标
java 事件监听 - 鼠标 //事件监听 //鼠标事件监听 //鼠标事件监听有两个实现接口 //1.MouseListener 普通的鼠标操作 //2.MouseMotionListener 鼠标的 ...
- bootstrap-滚动监听
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- Windows下python virtualenv使用,镜像源设置,批量安装,安装scipy,numpy
镜像源设置 在C:\Users\Administrator\下建立pip文件夹,然后在里面创建了一个pip.ini 内容为: [global]index-url = https://pypi.tuna ...
- OpenCV的数据类型---Cv
CvScalar就是一个包含四个元素的结构体变量. CvScalar定义可存放1—4个数值的数值,其结构如下. typedef struct CvScalar{ double val[4];}C ...
- 【VB6】打印机设置页边距
Printer.ScaleLeft = - Printer.ScaleTop = - Printer.CurrentX = Printer.CurrentY = '一些打印操作 Printer.End ...
- mysql load data 乱码的问题
新学mysql在用load data导入txt文档时发现导入的内容,select 之后是乱码,先后把表,数据库的字符集类型修改为utf8,但还是一样,最后在 http://bbs.chinaunix. ...