背景:我正在使用WPF和C#(3.5)并正在开发一个应用程序,允许用户查看已经是已编译程序集的一部分的表单/窗口/用户控件。当他们查看它时,他们应该能够点击任何控件(按钮,文本框,甚至是标签),控件应该出现一个小的弹出编辑器,然后他们可以在该控件中输入工具提示,helpID等。
它的长短:我需要在WPF中模仿基本的设计视图。这意味着我至少需要做以下几点:
首先,如果有人对更容易或更好的路线提出建议,请告诉我。 (显然WPF没有DesignHost类型的组件(就像我读过.NET 2一样),所以就这样了。)
我坚持使用粗体项目 - 清除任何订阅的EventHandler。在挖掘了一些并进入Reflector之后,我想出了这个很酷的危险代码(在这里,我只是想在XAML中定义一个叫做someButton的Button的所有EventHandlers):
<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,它返回:Object与目标类型不匹配(即,我的参数或目标对象被混乱)。我发现你可以解决这个问题:
GetEventHandlers.Invoke(Activator.CreateInstance(someButton.GetType()), Params);
// Also doesn't work...
当我在GetEventHandlers MethodInfo上设置监视时,它看起来很棒,它只是不喜欢我在调用Invoke时传递的内容。
我觉得我正处于如何获取RoutedEvent处理程序列表的最后一步(就像旧的GetInvocationList(),它显然不适用于WPF RoutedEvents)。从那里,它很简单,从每个控件中删除那些处理程序,并有一个无事件的表单,然后我可以添加自己的事件。
任何线索?同样,如果总体上有更好/更简单的方法来完成任务,请告诉我:)
答案 0 :(得分:3)
如果你采取不同的方法怎么办?您可以为所有事件调用EventManager.RegisterClassHandler(),然后在处理程序中(假设事件是设计图面上的控件,而不是UI的一部分)将事件标记为已处理。这样可以防止它被转发到设计图面上的控件,因为在标准事件处理程序之前调用类处理程序。
您仍然需要使用反射来获取控件提供的事件列表,但至少这样您就不会使用反射来删除事件。此外,如果您加载的程序集也注册了一个类处理程序(可能在您的代码之前),那么它们将首先被调用,但我猜这种情况很少发生。
答案 1 :(得分:3)
如果您使用GetEventHandlers.Invoke(EventHandlersStore , Params)
,它似乎运行良好且不会崩溃。
答案 2 :(得分: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); while(!(control is Window)&amp;&amp;!(control is Page)) { parent = VisualTreeHelper.GetParent(parent); }
然后,您可以使用该实例调用事件:
res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }