5

背景:我正在使用 WPF 和 C# (3.5),并且正在开发一个允许用户查看已经是已编译程序集的一部分的窗体/窗口/用户控件的应用程序。当他们查看它时,他们应该能够单击任何控件(按钮、文本框,甚至标签),控件旁边应该会出现一个小弹出编辑器,然后他们可以在其中输入该控件的工具提示、帮助 ID 等。

总而言之:我需要在 WPF 中模仿一个基本的设计视图。这意味着我至少需要执行以下操作:

  • 从给定程序集中加载用户控件/窗口(没问题)
  • 将它实例化为用户控件/窗口(没问题)
  • 清除其所有控件的所有订阅事件处理程序
  • 将我自己的“ShowEditorPopup”EventHandler 分配给每个控件(应该不是问题)

首先,如果有人对更简单或更好的路线提出建议,请告诉我。(显然 WPF 没有 DesignHost 类型的组件(就像我读过的 .NET 2 一样),所以它已经过时了。)

我卡在粗体项目上 - 清除所有订阅的 EventHandlers。在挖掘了一些并进入 Reflector 之后,我想出了这个很酷的危险代码块(在这里,我只是想获取在 XAML 中定义的名为 someButton 的单个按钮的所有事件处理程序):

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

这是代码(如果需要,您可以从 someButton_Click eventHandler 运行它):

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);
}

它适用于 Invoke,它返回: 对象与目标类型不匹配(即,我的参数或目标对象被弄乱了)。我发现您可以通过以下方式解决此问题:

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

当我在 GetEventHandlers MethodInfo 上设置手表时,它看起来很棒,只是不喜欢我在调用 Invoke 时传递的内容。

我觉得我正处于如何获取 RoutedEvent 处理程序列表的最后一步(比如旧的 GetInvocationList(),它显然不适用于 WPF RoutedEvents)。从那里,从每个控件中删除这些处理程序并拥有一个无事件的表单将非常简单,然后我可以将自己的事件添加到其中。

有什么线索吗?同样,如果有更好/更简单的方法来完成整个任务,请告诉我:)

4

3 回答 3

3

如果你采取不同的方法怎么办。您可以为所有事件调用EventManager.RegisterClassHandler(),然后在您的处理程序中(假设该事件用于设计图面上的控件,而不是 UI 的一部分)将事件标记为已处理。这应该可以防止它被转发到设计图面上的控件,因为类处理程序在标准事件处理程序之前被调用。

您仍然需要使用反射来获取控件提供的事件列表,但至少这样您就不会使用反射来删除事件。此外,如果您加载的程序集还注册了一个类处理程序(可能在您的代码之前),他们将首先被调用,但我猜这将是一种罕见的情况。

于 2009-06-12T11:11:04.833 回答
3

如果您使用GetEventHandlers.Invoke(EventHandlersStore , Params)它,它似乎运行良好并且不会崩溃。

于 2009-08-01T17:40:07.040 回答
1

使用上面的代码,我这样做了:

// 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); while (!(control is Window) && !(control is Page)) { parent = VisualTreeHelper.GetParent(parent); }

然后使用该实例,您可以调用该事件:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
于 2013-04-06T17:50:47.257 回答