Question

Contexte: J'utilise WPF et C # (3.5) et je travaille sur une application qui permet à un utilisateur d'afficher une forme / fenêtre / usercontrol qui est déjà partie d'un ensemble compilé. Quand ils le voient, ils devraient être en mesure de cliquer sur une commande (boutons, zones de texte, même les étiquettes), un petit éditeur de pop-up devrait apparaître par le contrôle où ils peuvent entrer dans une info-bulle, HelpID, etc., pour ce contrôle.

La longue et courte: je dois imiter une vue de la conception de base dans WPF. Ce qui signifie que je dois faire au moins les éléments suivants:

  • Charger la usercontrol / fenêtre à partir d'un assemblage donné (pas de problème)
  • instancier la usercontrol / fenêtre (pas de problème)
  • Effacer tous les gestionnaires d'événements pour tous les abonnés de ses commandes
  • Attribuer mon propre EventHandler "de ShowEditorPopup" à chaque commande (ne devrait pas être un problème)

Tout d'abord, si quelqu'un a des suggestions sur une plus facile ou mieux de prendre la route, s'il vous plaît laissez-moi savoir. (Apparemment, il n'y a pas de genre DesignHost de composants pour WPF (comme je l'ai lu .NET 2 a), de sorte que ce qui sort.)

Je suis bloqué sur l'élément en gras - compensation des gestionnaires d'événements souscrits. Après avoir creusé autour de certains et d'entrer dans réflecteur, je suis venu avec ce morceau frais code dangereux (ici, je suis juste essayer d'obtenir tous les gestionnaires d'événements pour un seul bouton appelé someButton définis dans le XAML):

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

Voici le code (vous pouvez l'exécuter à partir du someButton_Click eventHandler si vous voulez):

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

Il fonctionne jusqu'à Invoke, qui retourne: L'objet ne correspond pas au type de cible (c.-à-mes params ou objet cible sont foiré). Je l'ai trouvé, vous pouvez résoudre ce avec:

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

Quand je mets une garde sur la GetEventHandlers MethodInfo, il semble grand, il n'a tout simplement pas ce que je passe quand j'appelle Invoke.

Je sens que je suis à la dernière étape de la façon d'obtenir une liste des RoutedEvent Handlers (comme bon vieux GetInvocationList (), qui ne fonctionne pas pour RoutedEvents WPF, apparemment). De là, il sera assez simple pour enlever les Handlers de chaque contrôle et ont une forme eventless, que je peux alors ajouter mes propres événements.

Les indices? Encore une fois, s'il y a une meilleure / plus facile de faire la tâche globale, laissez-moi savoir:)

Était-ce utile?

La solution

Que faire si vous prenez une approche différente. Vous pouvez appeler EventManager.RegisterClassHandler () pour tous les événements, puis dans le gestionnaire (en supposant que l'événement est d'un contrôle sur la surface de conception, et ne fait pas partie de l'interface utilisateur) marquer l'événement comme traité. Cela devrait l'empêcher d'être transmis sur les commandes de votre surface de conception, car les gestionnaires de classe sont appelés avant gestionnaires d'événements standard.

Vous auriez toujours besoin d'utiliser la réflexion pour obtenir la liste des événements fournis par un contrôle, mais au moins de cette façon que vous ne comptez pas utiliser la réflexion pour supprimer les événements. En outre, si l'ensemble vous chargez enregistre également un gestionnaire de classe (probablement avant votre code ne), leur serait appelé en premier, mais je suppose que ce serait un événement rare.

Autres conseils

Si vous utilisez GetEventHandlers.Invoke(EventHandlersStore , Params) il semble bien fonctionner et ne tombe pas en panne.

En utilisant votre code d'en haut, je l'ai fait:

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

Une fois que vous avez alors que le seul problème est que vous avez maintenant l'info méthode, vous devez obtenir une instance à l'objet qui implémente cette méthode. Habituellement, les gestionnaires d'événements sont définis dans la fenêtre ou de l'objet page. Donc, pour l'obtenir:     parent var = VisualTreeHelper.GetParent (témoin);     while (! (contrôle est fenêtre) &&! (contrôle est la page))     {            parent = VisualTreeHelper.GetParent (parent);     }

Et avec cette instance, vous pouvez alors invoquer le wtih d'événement:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top