Pregunta

Antecedentes: Estoy usando WPF y C # (3.5) y estoy trabajando en una aplicación que permite al usuario ver una forma / ventana / control de usuario que ya parte de un conjunto compilado. Cuando lo ven, que debe ser capaz de hacer clic en cualquier control (botones, cuadros de texto, incluso etiquetas), un pequeño editor emergente debería aparecer por el control en el que se pueden escribir en un texto de ayuda, HelpID, etc., para ese control.

El largo y corto de él: tengo que imitar a una vista básica de diseño en WPF. Lo que significa que tengo que hacer al menos lo siguiente:

  • Cargar el usercontrol / ventana a partir de un conjunto dado (no hay problema)
  • Una instancia que el control de usuario / ventana (no hay problema)
  • Borrar todos los manejadores de sucesos suscritos para todos sus controles
  • Asignar mi propio manejador de sucesos "ShowEditorPopup" para cada control (no debería ser un problema)

En primer lugar, si alguien tiene sugerencias sobre una ruta más fácil o mejor que tomar, por favor hágamelo saber. (Al parecer no existe ningún tipo de componente DesignHost para WPF (como he leído .NET tiene 2), por lo que está fuera.)

Estoy atascado en el elemento en negrita - la liquidación de cualquier manejadores de sucesos suscritos. Después de cavar en torno a algunos y entrar en el reflector, me he dado con este pedazo fresco de código peligroso (en este caso, sólo estoy tratando de conseguir todos los manejadores de sucesos para un único botón de llamada someButton definidos en el XAML):

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

Aquí está el código (se puede ejecutar desde el someButton_Click eventHandler si lo desea):

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

Funciona hasta la invocación, que devuelve: El objeto no coincide con el tipo de destino (es decir, mis params objeto de destino o están en mal estado). He encontrado que puede resolver esto con:

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

Cuando me puse un reloj en la GetEventHandlers MethodInfo, se ve muy bien, sólo que no le gusta lo que estoy pasándolo cuando llamo al Invocar.

Me siento como si estuviera en el último paso de cómo obtener una lista de los manipuladores de RoutedEvent (como el bueno de GetInvocationList (), que no trabaja para RoutedEvents WPF, al parecer). A partir de ahí, va a ser lo suficientemente simple para eliminar esos Manipuladores de cada control y tienen una forma sin acontecimientos, que luego puedo añadir mis propios eventos a.

¿Alguna pista? De nuevo, si hay una manera mejor / más fácil de hacer la tarea general, que me haga saber:)

¿Fue útil?

Solución

¿Qué pasa si usted toma un enfoque diferente. Se le podría llamar EventManager.RegisterClassHandler () para todos los eventos, y luego en el controlador (suponiendo que el evento es para un control en la superficie de diseño, y no forma parte de la interfaz de usuario) marcar el evento como manejada. Esto debe evitar que sea remitido a los controles de la superficie de diseño, ya que los controladores de clase se llaman antes controladores de eventos estándar.

Se podría todavía tiene que utilizar la reflexión para obtener la lista de eventos proporcionados por un control, pero al menos de esta manera no estaría utilizando la reflexión para eliminar los eventos. Además, si la asamblea se carga también registra un controlador de clase (probablemente antes de que su código hace), la suya sería llamado primero, pero me imagino que esto sería un caso raro.

Otros consejos

Si utiliza GetEventHandlers.Invoke(EventHandlersStore , Params) parece que funciona bien y no se desplome.

Usando el código de arriba Hice esto:

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

Una vez que tenga que entonces el único problema es que ahora tiene la información método, por lo que necesita para obtener una instancia del objeto que implementa dicho método. Por lo general, los controladores de eventos se definen en la ventana u objeto de página. Así que para conseguirlo:     var padre = VisualTreeHelper.GetParent (control);     while (! (control es ventana) &&! (control es la página))     {            parent = VisualTreeHelper.GetParent (padre);     }

Y con esa instancia a continuación, puede invocar el caso wtih:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top