2009-06-11 41 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() }