轉(zhuǎn)帖|其它|編輯:郝浩|2011-08-11 14:57:00.000|閱讀 2977 次
概述:本文主要介紹C# 如何移除所有和事件關(guān)聯(lián)的委派函數(shù),希望對大家有幫助。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
有個關(guān)于事件的問題一直困擾著我,比方說代碼這樣寫
this.dgv_User.CellValueChanged -=
new System.Windows.Forms.DataGridViewCellEventHandler
(this.dgv_User_CellValueChanged);
//------------
//這里面又調(diào)用了另一個方法,這個方法里面也有這么兩句
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);
這樣的結(jié)果就是最后dgv的CellValueChanged被重復(fù)綁定了this.dgv_User_CellValueChanged事件,當(dāng) dgv的單元格值發(fā)生變化的時候就會觸發(fā)兩次,不管你綁了多少次這樣程序的效率就降低了.有個錯誤我要糾正,以前我以為C#中事件的訂閱不像VB那 樣,VB中直接RemoveHandler就能把所有綁定的方法移除,然后經(jīng)過測試發(fā)現(xiàn)VB跟C#是一樣的,也會重復(fù)綁定,RemoveHandler也 只能一次一次的取消綁定怎么避免這個問題呢?
方法一:人為的控制dgv的CellValueChanged事件被訂閱的次數(shù),移除一個,綁定一 個;這種方式只能做到盡可能降低事件被重復(fù)訂閱的幾率,因?yàn)楫?dāng)一個程序比較復(fù)雜的時候,難免會發(fā)生在一個函數(shù)(包含事件的訂閱和取消訂閱注銷)里面嵌套另 一個函數(shù)(包含事件的訂閱和取消訂閱注銷),這樣仍然會導(dǎo)致性能下降,久而久之變成頑疾。
方法二:讓我們來一起研究我寫了這樣一個事件
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);
事件本質(zhì)上是一個MulticastDelegate對象,所以使用MulticastDelegate的GetInvocationList 方法可以得到事件被訂閱的列表,也就是調(diào)用這個事件的函數(shù)列表,然后用count返回所有的被訂閱次數(shù)。想到這里,我們是不是可以重寫 DataGridview控件,加上一個監(jiān)控CellValueChanged被訂閱次數(shù)的方法,現(xiàn)在我們來試下。
public class CustomDataGridView : DataGridView
{
public int GetEventCount()
{
if (this.CellValueChanged == null)
return 0;
else
return CellValueChanged.GetInvocationList().Count();
}
}
編譯一下,發(fā)現(xiàn)報(bào)錯。原因是錯誤 事件“System.Windows.Forms.DataGridView.CellValueChanged”只能出現(xiàn)在 += 或 -= 的左邊 。原來雖然CellValueChanged事件雖然是MulticastDelegate對象,但是如果要使用 MulticastDelegate的GetInvocationList方法卻只能在最初定義CellValueChanged事件的類的內(nèi)部才能使 用,就像我寫的TestEvent里面,因?yàn)槭录莾?nèi)部定義的,所以能夠使用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);
程序跑起來,卻發(fā)現(xiàn)無論怎么改變單元格的值都不能觸發(fā)dgv_User_CellValueChangedNew事件,看來這么做還是不行啊,至于為什么沒 有觸發(fā)這個事件我也很納悶,有經(jīng)驗(yàn)的朋友可以教教我。后來在csdn上發(fā)帖,也沒有得到很大的幫助,就在這個時候,向技術(shù)總監(jiān)王總發(fā)出求助,他給了一個貼 子,剛好可以解決我的問題,用的是反射。這是那個帖子的源碼
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參數(shù),如果是DataGridView的事件都要是這種格式"EVENT_" + "DATAGRIDVIEW" + "CELLVALUECHANGED"(event+dgv+大寫的事件名),不然返回的都是null。
大功告成,現(xiàn)在可以用temp的MulticastDelegate對象的方法得到你想要的東西,單個取消訂閱、取消所有訂閱、得到所有被訂閱的次數(shù)、訂閱該事件的所有函數(shù)等等等等信息。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客園