Domanda

Background: Sto utilizzando WPF e C # (3,5) e sto lavorando su un app che consente a un utente di visualizzare un form / finestra / UserControl che è già parte di un assembly compilato. Quando lo vedono, essi dovrebbero essere in grado di fare clic su qualsiasi controllo (pulsanti, caselle di testo, anche etichette), un editor di piccolo pop-up dovrebbe essere visualizzato dal controllo dove possono quindi digitare un tooltip, HelpID, ecc, per quel controllo.

Il lungo e breve di esso: Ho bisogno di imitare una vista disegno di base in WPF. Il che significa che ho bisogno di fare almeno le seguenti:

  • Caricare l'usercontrol / finestra da un dato complesso (nessun problema)
  • un'istanza che l'UserControl / finestra (nessun problema)
  • Cancella tutti EventHandlers sottoscritti per tutti i suoi controlli
  • Assegna mia EventHandler "ShowEditorPopup" per ogni controllo (non dovrebbe essere un problema)

Prima di tutto, se qualcuno ha suggerimenti su un percorso più facile o meglio prendere, per favore fatemelo sapere. (A quanto pare non esiste nessun tipo DesignHost di componenti per WPF (come ho letto NET 2 ha), in modo che è fuori.)

Sono bloccato sulla voce in grassetto - eliminare eventuali EventHandlers sottoscritti. Dopo aver scavato intorno ad alcuni e di entrare in Reflector, mi è venuta in mente questo pezzo fresco di codice pericoloso (qui, sto solo cercando di ottenere tutti i EventHandlers per un solo pulsante chiamato someButton definiti nel XAML):

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

Ecco il codice (è possibile eseguirlo dal someButton_Click EventHandler se volete):

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

Si lavora fino al Invoke, che restituisce: L'oggetto non corrisponde al tipo di destinazione (ad esempio, i miei params o oggetto di destinazione sono in disordine). Ho trovato è possibile risolvere questo con:

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

Quando ho creato un orologio sul GetEventHandlers MethodInfo, sembra grande, semplicemente non piace quello che sto passando quando chiamo l'Invoke.

Mi sento come se fossi all'ultimo passo di come ottenere un elenco dei gestori RoutedEvent (come il buon vecchio GetInvocationList (), che non funziona per RoutedEvents WPF, a quanto pare). Da lì, sarà abbastanza semplice per rimuovere quelle gestori da ogni controllo e hanno una forma priva di eventi, che posso poi aggiungere i miei eventi.

Degli indizi? Anche in questo caso, se c'è un / modo migliore più facile fare il compito generale, fatemelo sapere:)

È stato utile?

Soluzione

Che cosa succede se si prende un approccio diverso. Si potrebbe chiamare EventManager.RegisterClassHandler () per tutti gli eventi, e poi nel vostro gestore (supponendo che l'evento è per un controllo nell'area di progettazione, e non fa parte della vostra UI) celebrare l'evento come gestito. Questo dovrebbe impedire che vengano inoltrati a controlli sulla superficie di progettazione, dal momento che i gestori di classe sono chiamati prima di gestori di eventi standard.

Si sarebbe ancora bisogno di utilizzare la reflection per ottenere l'elenco degli eventi forniti da un controllo, ma almeno in questo modo non sarebbe utilizzando riflessione per rimuovere gli eventi. Inoltre, se l'assemblea si carica anche registra un gestore di classe (probabilmente prima del tuo codice fa), il loro sarebbe stato chiamato prima, ma direi che questo sarebbe un evento raro.

Altri suggerimenti

Se si utilizza GetEventHandlers.Invoke(EventHandlersStore , Params) sembra funzionare bene e non si blocca.

Utilizzando il codice da sopra ho fatto questo:

// 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 volta che avete che allora l'unico problema è che ora avete le informazioni metodo, quindi è necessario ottenere un'istanza per l'oggetto che implementa tale metodo. Di solito i gestori di eventi sono definiti sulla finestra o dell'oggetto Page. Quindi, per ottenerlo:     var parent = VisualTreeHelper.GetParent (controllo);     while (! (il controllo è Window) &&! (controllo la pagina))     {            parent = VisualTreeHelper.GetParent (padre);     }

E con questo esempio si può quindi invocare l'addebbitato evento:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top