Background: I'm using WPF and C# (3.5) and am working on an app that allows a user to view a form/window/usercontrol that's already part of a compiled assembly. When they view it, they should be able to click on any control (buttons, textboxes, even labels), a little popup editor should appear by the control where they can then type in a tooltip, helpID, etc., for that control.
The long and short of it: I need to imitate a basic design view in WPF. Which means I need to do at least the following:
- Load the usercontrol/window from a given assembly (no problem)
- Instantiate it the usercontrol/window (no problem)
- Clear all subscribed EventHandlers for all its controls
- Assign my own "ShowEditorPopup" EventHandler to each control (shouldn't be a problem)
First off, if anybody has suggestions on an easier or better route to take, please let me know. (Apparently there is no DesignHost kind of component for WPF (like I've read .NET 2 has), so that's out.)
I'm stuck on the bolded item - clearing any subscribed EventHandlers. After digging around some and getting into Reflector, I've come up with this cool chunk of dangerous code (here, I'm just trying to get all the EventHandlers for a single Button called someButton defined in the XAML):
<Button Name="someButton" Click="someButton_Click"/>
Here's the code (you can run it from the someButton_Click eventHandler if you want):
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);
}
It works up to the Invoke, which returns: Object does not match target type (ie, my params or target object are messed). I've found you can resolve this with:
GetEventHandlers.Invoke(Activator.CreateInstance(someButton.GetType()), Params);
// Also doesn't work...
When I set a watch on the GetEventHandlers MethodInfo, it looks great, it just doesn't like what I'm passing it when I call the Invoke.
I feel like I'm at the very last step of how to get a list of the RoutedEvent Handlers (like good old GetInvocationList(), which doesn't work for WPF RoutedEvents, apparently). From there, it'll be simple enough to remove those Handlers from each control and have an eventless form, which I can then add my own events to.
Any clues? Again, if there's a better/easier way to do the task overall, let me know :)