2009-06-11 81 views
3

背景:我正在使用WPF和C#(3.5),並且正在開發一個應用程序,該應用程序允許用戶查看已經是編譯程序集的一部分的窗體/窗口/用戶控件。當他們查看它時,他們應該能夠點擊任何控件(按鈕,文本框,甚至標籤),控件上應該出現一個小小的彈出窗口編輯器,然後可以在該控件中輸入工具提示,helpID等。通過反射去除路由事件處理程序?

它的長短:我需要模仿WPF中的基本設計視圖。這意味着我至少需要做到以下幾點:

  • 負載從一個給定的裝配(沒問題)的用戶控件/窗口
  • 中實例化用戶控件/窗口(沒問題)
  • 清除其所有控制
  • 指定我自己的「ShowEditorPopup」事件處理程序,以每個控制所有訂閱的事件處理器(不應該是一個問題)

首先,如果有人對更簡單或更好的路線有任何建議,請讓我知道。 (顯然,WPF沒有DesignHost類型的組件(就像我已經讀過.NET 2一樣),所以這是不可能的。)

我被困在粗體項目 - 清除任何訂閱的EventHandlers。在挖掘了一些並進入Reflector之後,我想出了這個很酷的危險代碼塊(在這裏,我只是想讓所有的EventHandlers在XAML中定義一個叫someButton的Button):

<Button Name="someButton" Click="someButton_Click"/> 

下面的代碼(你可以,如果你想從someButton_Click事件處理程序運行它):

public void SomeFunction() 
{ 
// Get the control's Type 
Type someButtonType = ((UIElement)someButton).GetType(); 

// Dig out the undocumented (yes, I know, it's risky) EventHandlerStore 
// from the control's Type 
PropertyInfo EventHandlersStoreType = 
     someButtonType.GetProperty("EventHandlersStore", 
     BindingFlags.Instance | BindingFlags.NonPublic); 

// Get the actual "value" of the store, not just the reflected PropertyInfo 
Object EventHandlersStore = EventHandlersStoreType.GetValue(someButton, null); 

// Get the store's type ... 
Type storeType = EventHandlersStore.GetType(); 

// ... so we can pull out the store's public method GetRoutedEventHandlers 
MethodInfo GetEventHandlers = 
     storeType.GetMethod("GetRoutedEventHandlers", 
     BindingFlags.Instance | BindingFlags.Public); 

// Reflector shows us that the method's sig is this: 
// public RoutedEventHandlerInfo[] GetRoutedEventHandlers(RoutedEvent routedEvent); 

// So set up the RoutedEvent param 
object[] Params = new object[] { ButtonBase.ClickEvent as RoutedEvent }; 
// I've also seen this for the param, but doesn't seem to make a difference: 
// object[] Params = new object[] { someButton.ClickEvent }; 

// And invoke it ... and watch it crash! 
GetEventHandlers.Invoke(someButton, Params); 
} 

它的工作原理到調用,它返回:對象不匹配目標類型(即我params中,目標對象被弄亂)。我發現你可以解決此問題:

GetEventHandlers.Invoke(Activator.CreateInstance(someButton.GetType()), Params); 
// Also doesn't work... 

當我設置監視那個GetEventHandlers的MethodInfo,它看起來很棒,它只是不喜歡什麼,我路過它時,我所說的調用。

我覺得我在最後一步如何獲得RoutedEvent處理程序的列表(如舊的GetInvocationList(),顯然不適用於WPF RoutedEvents)。從那裏,它將會非常簡單,從每個控件中移除這些處理程序,並擁有一個無事件形式,然後我可以添加自己的事件。

任何線索?再次,如果有更好/更簡單的方法來完成整個任務,請讓我知道:)

+0

哇,你是一個偉大的工作,我只是想這樣做。你只是給了我需要的出發點。 – orellabac 2013-04-06 17:44:32

回答

3

如果採取不同的方法,該怎麼辦?您可以爲所有事件調用EventManager.RegisterClassHandler(),然後在您的處理程序中(假設事件用於設計界面上的控件,而不是用戶界面的一部分),將事件標記爲已處理。這應該防止它被轉發到設計表面上的控件,因爲在標準事件處理程序之前調用類處理程序。

您仍然需要使用反射來獲取控件提供的事件列表,但至少您不會使用反射來移除事件。另外,如果你加載的程序集也註冊了一個類處理程序(可能在你的代碼執行之前),他們會首先被調用,但我猜想這是一種罕見的情況。

+0

很棒的建議。我想知道是否可以通過設置Handled = true來阻止其他事件的觸發,但我不知道「Class Handlers」是否被首先觸發。 我會試試看。謝謝! – Eddie 2009-06-12 21:45:54

3

如果您使用GetEventHandlers.Invoke(EventHandlersStore , Params)它似乎運作良好,不會崩潰。

0

使用你的代碼從上面我這樣做:

// Get the control's Type 
Type controlViewType = ((UIElement)control).GetType(); 

// Dig out the undocumented (yes, I know, it's risky) EventHandlerStore 
// from the control's Type 
PropertyInfo EventHandlersStoreType = 
controlViewType.GetProperty("EventHandlersStore", 
BindingFlags.Instance | BindingFlags.NonPublic); 

// Get the actual "value" of the store, not just the reflected PropertyInfo 
Object EventHandlersStore = EventHandlersStoreType.GetValue(tree, null); 
var miGetRoutedEventHandlers 
EventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", 
BindingFlags.Public | BindingFlags.Instance); 
RoutedEventHandlerInfo[] res = 
(RoutedEventHandlerInfo[])miGetRoutedEventHandlers.Invoke(EventHandlersStore, 
new object[] { CheckedTreeViewItem.CheckedEvent }); 

一旦你有那麼唯一的問題是,你現在有方法的信息,所以你需要得到一個實例來實現該目標方法。通常事件處理程序是在Window或Page對象上定義的。所以得到它: var parent = VisualTreeHelper.GetParent(control); (控制是Window)& &!(對照是Page)) parent = VisualTreeHelper.GetParent(parent); }

以及與該實例然後你可以調用事件wtih:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }