2012年2月7日 星期二

C# 如何移除未知名稱的事件 - BarButtonItem.Click


上次有寫過,
如果傳進來的 BindingSource 有 PositionChanged 的事件的時候就去觸發它!
請參閱【C# 如何強制驅動物件的事件?】。

這次不一樣的是,如果傳進來的 BarButtonItem 有 ItemClick 的事件的話,
我要將它移除,然後重新掛上自己的事件。
為什麼會有這樣的需求呢?
因為在底層的查詢按鈕直接將事件寫在 ItemClick 上面了,
而我想要拿這個按鈕來用卻又不想要執行底層的程式,
所以我需要將原來 delegate 的移除之後再重新掛上我的 ItemClick 程式。
緣由說明完畢啦!接下來就要實做了,
依照上次【C# 如何強制驅動物件的事件?】的做法居然得不到 FieldInfo !
google 了很久,後來在 DevExpress 的討論區裡面看到一個範例,
雖然是 VB 有點看不太懂,
後來我照著翻譯成 C# 之後也是可以跑的,
程式碼如下:


private void ResetBarItemClickEvent(DevExpress.XtraBars.BarButtonItem btn)
{
    FieldInfo itemClickInfo = (typeof(DevExpress.XtraBars.BarItem)).GetField("itemClick",
        BindingFlags.Static | BindingFlags.NonPublic);

    FieldInfo eventsInfo = (typeof(Component)).GetField("events",
        BindingFlags.Instance | BindingFlags.NonPublic);

    /*
     * 測試之後 itemClickInfo.GetValue(btn)
     * itemClickInfo.GetValue(null) 得到的都一樣,
     * 不知道差別是甚麼?
     */
    object itemClick = itemClickInfo.GetValue(btn);
    //object itemClick = itemClickInfo.GetValue(null);

    EventHandlerList events = eventsInfo.GetValue(btn) as EventHandlerList;

    if (events == null) return;

    ItemClickEventHandler handler = events[itemClick] as ItemClickEventHandler;

    Delegate d = events[itemClick];

    if (d != null)
    {
        events.RemoveHandler(itemClick, d);
    }

    btn.ItemClick += new ItemClickEventHandler(btn_ItemClick);
}


重點說明如下:

由於 ItemClick 的事件是掛在 BarItem 下面的,所以必須由 BarItem 去 GetField ,
然後關鍵的 "itemClick" 也在 BarItem 下面。如下的程式碼與圖所示。


    FieldInfo itemClickInfo = (typeof(DevExpress.XtraBars.BarItem)).GetField("itemClick"
        BindingFlags.Static | BindingFlags.NonPublic);





用 FieldInfo.GetValue() 可以得到那個事件的物件指標。

    object itemClick = itemClickInfo.GetValue(btn);

用下面兩行程式取得 EventHandlerList 。


    FieldInfo eventsInfo = (typeof(Component)).GetField("events",
        BindingFlags.Instance | BindingFlags.NonPublic);


    EventHandlerList events = eventsInfo.GetValue(btn) as EventHandlerList;

最後用 itemClick 物件指標在 EventHandlerList 裡面去取得 ItemClick 的 delegate。

    Delegate d = events[itemClick];

得到這個 delegate 之後我們就可以把它移除,

        events.RemoveHandler(itemClick, d);

然後加上自己的程式了!

    btn.ItemClick += new ItemClickEventHandler(btn_ItemClick);

收攤!

相關資料
How can I test if BarItem has click event associated with it and if doesn't disable the item
Location BarButtonItem of a PopupMenu
How do you use reflection to get the invocation list of an event delegate in a BarButtonItem?
C# 移除事件 <-這一篇寫得不錯,很值得參考!

2 則留言:

  1. 就是不能丫!
    用 += 的方式只能累加上去,
    沒辦法 override 啊!

    回覆刪除