提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
转帖|其它|编辑:郝浩|2011-08-11 14:57:00.000|阅读 2948 次
概述:本文主要介绍C# 如何移除所有和事件关联的委派函数,希望对大家有帮助。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
有个关于事件的问题一直困扰着我,比方说代码这样写
this.dgv_User.CellValueChanged -=
new System.Windows.Forms.DataGridViewCellEventHandler
(this.dgv_User_CellValueChanged);
//------------
//这里面又调用了另一个方法,这个方法里面也有这么两句
this.dgv_User.CellValueChanged -=
new System.Windows.Forms.DataGridViewCellEventHandler
(this.dgv_User_CellValueChanged);
this.dgv_User.CellValueChanged +=
new System.Windows.Forms.DataGridViewCellEventHandler
(this.dgv_User_CellValueChanged);
//------------
this.dgv_User.CellValueChanged +=
new System.Windows.Forms.DataGridViewCellEventHandler
(this.dgv_User_CellValueChanged);
这样的结果就是最后dgv的CellValueChanged被重复绑定了this.dgv_User_CellValueChanged事件,当 dgv的单元格值发生变化的时候就会触发两次,不管你绑了多少次这样程序的效率就降低了.有个错误我要纠正,以前我以为C#中事件的订阅不像VB那 样,VB中直接RemoveHandler就能把所有绑定的方法移除,然后经过测试发现VB跟C#是一样的,也会重复绑定,RemoveHandler也 只能一次一次的取消绑定怎么避免这个问题呢?
方法一:人为的控制dgv的CellValueChanged事件被订阅的次数,移除一个,绑定一 个;这种方式只能做到尽可能降低事件被重复订阅的几率,因为当一个程序比较复杂的时候,难免会发生在一个函数(包含事件的订阅和取消订阅注销)里面嵌套另 一个函数(包含事件的订阅和取消订阅注销),这样仍然会导致性能下降,久而久之变成顽疾。
方法二:让我们来一起研究我写了这样一个事件
public class TestEvent
{
public delegate void DataGridViewCellEventNewHandler
(object sender, DataGridViewCellEventArgs e);
public event DataGridViewCellEventNewHandler CellValueChangedNew;
public int GetEventCount()
{
if (CellValueChangedNew == null)
return 0;
else
return CellValueChangedNew.GetInvocationList().Count();
}
}
注意,这里的DataGridViewCellEventNewHandler不是DataGridView的DataGridViewCellEventHandler;然后在程序中使用这个类的事件
TestEvent testEvent = new TestEvent();
testEvent.CellValueChangedNew -= new TestEvent.DataGridViewCellEventNewHandler(this.dgv_User_CellValueChanged);
if (testEvent.GetEventCount() == 0)
testEvent.CellValueChangedNew += new TestEvent.DataGridViewCellEventNewHandler(this.dgv_User_CellValueChanged);
事件本质上是一个MulticastDelegate对象,所以使用MulticastDelegate的GetInvocationList 方法可以得到事件被订阅的列表,也就是调用这个事件的函数列表,然后用count返回所有的被订阅次数。想到这里,我们是不是可以重写 DataGridview控件,加上一个监控CellValueChanged被订阅次数的方法,现在我们来试下。
public class CustomDataGridView : DataGridView
{
public int GetEventCount()
{
if (this.CellValueChanged == null)
return 0;
else
return CellValueChanged.GetInvocationList().Count();
}
}
编译一下,发现报错。原因是错误 事件“System.Windows.Forms.DataGridView.CellValueChanged”只能出现在 += 或 -= 的左边 。原来虽然CellValueChanged事件虽然是MulticastDelegate对象,但是如果要使用 MulticastDelegate的GetInvocationList方法却只能在最初定义CellValueChanged事件的类的内部才能使 用,就像我写的TestEvent里面,因为事件是内部定义的,所以能够使用GetInvocationList方法。既然不能用他写的事件,那么我们自 己写一个CellValueChanged不行吗?试一下
public class CustomDataGridView : DataGridView
{
public delegate void DataGridViewCellEventNewHandler
(object sender, DataGridViewCellEventArgs e);
public event DataGridViewCellEventNewHandler CellValueChangedNew;
public int GetEventCount()
{
if (CellValueChangedNew == null)
return 0;
else
return CellValueChangedNew.GetInvocationList().Count();
}
}
重写了DataGridview,自己定义了CellValueChanged事件,控件写好了,看看能不能用
this.dgv_User.CellValueChangedNew+=
new CustomDataGridView.DataGridViewCellEventNewHandler
(dgv_User_CellValueChangedNew);
程序跑起来,却发现无论怎么改变单元格的值都不能触发dgv_User_CellValueChangedNew事件,看来这么做还是不行啊,至于为什么没 有触发这个事件我也很纳闷,有经验的朋友可以教教我。后来在csdn上发帖,也没有得到很大的帮助,就在这个时候,向技术总监王总发出求助,他给了一个贴 子,刚好可以解决我的问题,用的是反射。这是那个帖子的源码
EventHandlerList buttonEvents =
(EventHandlerList)this.button1.GetType().InvokeMember("Events", System.Reflection.BindingFlags.GetProperty
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic, null, this.button1, null);
buttonEvents.GetType().GetMethod("button1_Click");
PropertyInfo propertyInfo =
(typeof(System.Windows.Forms.Button)).GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
EventHandlerList eventHandlerList =
(EventHandlerList)propertyInfo.GetValue(button1, null);
FieldInfo fieldInfo =
(typeof(Control)).GetField("EventClick", BindingFlags.Static | BindingFlags.NonPublic);
Delegate d = eventHandlerList[fieldInfo.GetValue(null)];
if (d != null)
{
foreach (Delegate temp in d.GetInvocationList())
{
Console.WriteLine(temp.Method.Name);
//这个地方可以清除所有的委托,也可以使用条件清除指定委托,没有办法直接清除所有的
}
}
C#的Control封装了EventHandlerList, 但它是protected的, 所以我们不能简单的看到它的存在, 不过, 如果你走Debug Mode的话, 还是可以看得很清楚的, 但如果真要把它拿出来用, 就只能用Reflect了
上面的代码是button的,怎么用到DataGridview上来,只要稍加改动
private void ClearEvent()
{
PropertyInfo propertyInfo =
(typeof(System.Windows.Forms.CustomDataGridView)).
GetProperty("Events", BindingFlags.Instance |
BindingFlags.NonPublic);
EventHandlerList eventHandlerList =
(EventHandlerList)propertyInfo.GetValue(dgv_User, null);
FieldInfo fieldInfo = (typeof(DataGridView)).GetField("EVENT_" + "DATAGRIDVIEW" + "CELLVALUECHANGED", BindingFlags.Static |
BindingFlags.NonPublic);
Delegate d = eventHandlerList[fieldInfo.GetValue(this.dgv_User)];
if (d != null)
{
foreach (Delegate temp in d.GetInvocationList())
{
//这个地方可以清除所有的委托,也可以使用条件清除指定委托,没有办法直接清除所有的
}
}
}
这里要注意的是GetField方法里面的string name参数,如果是DataGridView的事件都要是这种格式"EVENT_" + "DATAGRIDVIEW" + "CELLVALUECHANGED"(event+dgv+大写的事件名),不然返回的都是null。
大功告成,现在可以用temp的MulticastDelegate对象的方法得到你想要的东西,单个取消订阅、取消所有订阅、得到所有被订阅的次数、订阅该事件的所有函数等等等等信息。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn
文章转载自:博客园面对“数字中国”建设和中国制造2025战略实施的机遇期,中车信息公司紧跟时代的步伐,以“集约化、专业化、标准化、精益化、一体化、平台化”为工作目标,大力推进信息服务、工业软件等核心产品及业务的发展。在慧都3D解决方案的实施下,清软英泰建成了多模型来源的综合轻量化显示平台、实现文件不失真的百倍压缩比、针对模型中的大模型文件,在展示平台上进行流畅展示,提升工作效率,优化了使用体验。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@pclwef.cn
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢