Condividi tramite


Contrassegno degli eventi indirizzati come gestiti e gestione delle classi

I gestori per un evento indirizzato possono contrassegnare l'evento come gestito all'interno dei dati di evento. La gestione dell'evento consente di abbreviare efficacemente la route. La gestione delle classi è un concetto di programmazione supportato dagli eventi indirizzati. Un gestore di classi ha la possibilità di gestire un determinato evento indirizzato a livello di classe con un gestore richiamato prima di qualsiasi gestore di istanze su qualsiasi istanza della classe.

Nel presente argomento sono contenute le seguenti sezioni.

  • Prerequisiti
  • Quando contrassegnare gli eventi come gestiti
  • Eventi di anteprima (tunneling),eventi di bubbling e gestione degli eventi
  • Gestori di classi e gestori di istanze
  • Gestione di classi di eventi indirizzati tramite le classi di base dei controlli
  • Aggiunta di gestori di istanze generati anche quando gli eventi sono contrassegnati come gestiti
  • Eliminazione intenzionale degli eventi di input per la composizione dei controlli
  • Argomenti correlati

Prerequisiti

In questo argomento vengono illustrati i concetti introdotti in Cenni preliminari sugli eventi indirizzati.

Quando contrassegnare gli eventi come gestiti

Quando si imposta il valore della proprietà Handled su true nei dati di evento per un evento indirizzato, questa operazione viene definita "contrassegno dell'evento come gestito". Non esiste una regola assoluta per stabilire quando è necessario contrassegnare gli eventi indirizzati come gestiti, sia come autore di applicazioni sia come autore di controlli che risponde a eventi indirizzati esistenti o ne implementa di nuovi. Nella maggior parte dei casi, il concetto di gestito come incluso nei dati dell'evento indirizzato deve essere utilizzato come protocollo limitato per le risposte di un'applicazione personalizzata ai vari eventi indirizzati esposti nelle APIs WPF e per qualsiasi evento indirizzato personalizzato. Un altro approccio per affrontare questo concetto consiste nel considerare che generalmente è necessario contrassegnare un evento indirizzato come gestito se il codice ha risposto all'evento indirizzato in modo significativo e relativamente completo. Generalmente, non dovrebbe essere presente più di una risposta significativa che richiede implementazioni di gestori separati per ogni singola occorrenza dell'evento indirizzato. Se sono necessarie più risposte, il codice necessario deve essere implementato tramite la logica dell'applicazione concatenata all'interno di un unico gestore, anziché con il sistema di eventi indirizzati per l'inoltro. Anche il concetto di ciò che è "significativo" è soggettivo e dipende dall'applicazione o dal codice. Come indicazione generale, sono esempi di "risposta significativa" l'impostazione dello stato attivo, la modifica dello stato pubblico, l'impostazione delle proprietà che influiscono sulla rappresentazione visiva e la generazione di altri eventi nuovi. Sono invece esempi di risposte non significative la modifica dello stato privato (senza impatto visivo o rappresentazione a livello di codice), la registrazione di eventi o l'analisi di argomenti di un evento e la scelta di non rispondere.

Il comportamento del sistema di eventi indirizzati consolida questo modello di "risposta significativa" per l'utilizzo dello stato gestito di un evento indirizzato, poiché i gestori aggiunti in XAML o la firma comune di AddHandler non vengono richiamati in risposta a un evento indirizzato in cui i dati di evento sono già contrassegnati come gestiti. È necessario eseguire un'ulteriore operazione, aggiungendo un gestore con la versione del parametro handledEventsToo (AddHandler(RoutedEvent, Delegate, Boolean)) per gestire gli eventi indirizzati contrassegnati come gestiti dai partecipanti precedenti nella route dell'evento.

In alcune circostanze, sono i controlli stessi che contrassegnano determinati eventi indirizzati come gestiti. Un evento indirizzato gestito rappresenta una decisione degli autori dei controlli WPF in base alla quale le azioni del controllo in risposta all'evento indirizzato sono significative o complete come parte dell'implementazione del controllo e l'evento non necessita di ulteriore gestione. Di solito, questa operazione viene eseguita aggiungendo un gestore di classi per un evento oppure eseguendo l'override di uno degli elementi virtuali del gestore di classi presenti in una classe di base. Se necessario, è possibile ovviare alla gestione degli eventi. Vedere Soluzione per l'eliminazione degli eventi da parte dei controlli più avanti in questo argomento.

Eventi di anteprima (tunneling),eventi di bubbling e gestione degli eventi

Gli eventi indirizzati di anteprima sono eventi che seguono una route di tunneling tramite la struttura ad albero degli elementi. Il termine "Preview" nella convenzione di denominazione è indicativo del principio generale per gli eventi di input in base al quale gli eventi indirizzati di anteprima (tunneling) vengono generati prima dell'evento indirizzato di bubbling equivalente. Inoltre, gli eventi indirizzati di input che dispongono di una coppia di tunneling e bubbling presentano una logica di gestione distinta. Se l'evento indirizzato di tunneling/anteprima viene contrassegnato come gestito da un listener di eventi, l'evento indirizzato di bubbling viene contrassegnato come gestito addirittura prima che qualsiasi listener dell'evento indirizzato di bubbling lo riceva. Gli eventi indirizzati di tunneling e di bubbling sono eventi tecnicamente separati, ma condividono intenzionalmente la stessa istanza dei dati di evento per abilitare questo comportamento.

La connessione tra gli eventi indirizzati di tunneling e di bubbling viene effettuata dall'implementazione interna del modo in cui qualsiasi classe WPF specificata genera eventi indirizzati dichiarati propri, una condizione che viene soddisfatta per gli eventi indirizzati di input abbinati. A meno che non sia presente questa implementazione a livello di classe, tuttavia, non esiste connessione tra un evento indirizzato di tunneling e un evento indirizzato di bubbling che condividono lo schema di denominazione. Senza tale implementazione, i due eventi indirizzati sarebbero completamente separati e non verrebbero generati in sequenza, né condividerebbero dati di evento.

Per ulteriori informazioni sull'implementazione delle coppie di eventi indirizzati di input di tunneling e bubbling in una classe personalizzata, vedere Procedura: creare un evento indirizzato personalizzato.

Gestori di classi e gestori di istanze

Gli eventi indirizzati considerano due tipi diversi di listener per l'evento: listener di classi e listener di istanze. I listener di classi esistono poiché i tipi hanno chiamato un determinato oggetto EventManagerAPI, RegisterClassHandler, nel costruttore statico oppure hanno eseguito l'override di un metodo virtuale di gestore di classi dalla classe di base di un elemento. I listener di istanze sono elementi/istanze di classe particolari in cui sono stati associati uno o più gestori per tale evento indirizzato mediante una chiamata a AddHandler. Gli eventi indirizzati WPF esistenti effettuano chiamate a AddHandler come parte delle implementazioni add{} e remove{} del wrapper di eventi common language runtime (CLR) dell'evento, che costituisce anche la modalità di abilitazione del semplice meccanismo XAML per associare i gestori di eventi tramite una sintassi degli attributi. Pertanto, anche il semplice utilizzo di XAML equivale in definitiva a una chiamata a AddHandler.

Gli elementi all'interno della struttura ad albero visuale vengono controllati per verificare la presenza di implementazioni di gestori registrate. I gestori vengono richiamati potenzialmente in tutta la route, nell'ordine inerente al tipo di strategia di routing per un determinato evento indirizzato. Ad esempio, gli eventi indirizzati di bubbling richiamano innanzitutto i gestori associati allo stesso elemento che ha generato l'evento indirizzato. L'evento indirizzato viene quindi propagato all'elemento padre successivo e così via, finché non viene raggiunto l'elemento radice dell'applicazione.

Dalla prospettiva dell'elemento radice in una route di bubbling, se la gestione delle classi o qualsiasi elemento più vicino all'origine dell'evento indirizzato richiama i gestori che contrassegnano gli argomenti dell'evento come gestiti, i gestori negli elementi radice non vengono richiamati e la route dell'evento viene efficacemente abbreviata prima di raggiungere tale elemento radice. Tuttavia, la route non viene arrestata completamente, poiché i gestori possono essere aggiunti utilizzando una condizione speciale in base alla quale dovranno essere ancora richiamati, anche se un gestore di classi o un gestore di istanze ha contrassegnato l'evento indirizzato come gestito. Questa situazione viene illustrata nella sezione Aggiunta di gestori di istanze generati anche quando gli eventi sono contrassegnati come gestiti, più avanti in questo argomento.

A un livello più profondo rispetto alla route dell'evento, esistono potenzialmente più gestori di classi che operano su qualsiasi istanza specificata di una classe. Per questo motivo il modello di gestione delle classi per gli eventi indirizzati consente a tutte le classi possibili in una gerarchia di classi di registrare ciascuna il proprio gestore di classi per ciascun evento indirizzato. Ogni gestore di classi viene aggiunto a un archivio interno e, quando viene costruita la route dell'evento per un'applicazione, tutti i gestori di classi vengono aggiunti alla route dell'evento. I gestori di classi vengono aggiunti alla route in modo che il gestore di classi maggiormente derivato venga richiamato per primo e, successivamente, vengano richiamati i gestori di classi da ogni classe di base successiva. In genere, i gestori di classi non vengono registrati in modo che rispondano anche a eventi indirizzati già contrassegnati come gestiti. Pertanto, questo meccanismo di gestione delle classi consente una delle due scelte seguenti:

  • Le classi derivate possono integrare la gestione delle classi ereditata dalla classe di base aggiungendo un gestore che non contrassegna l'evento indirizzato come gestito, poiché il gestore della classe di base verrà richiamato dopo il gestore delle classi derivate.

  • Le classi derivate possono sostituire la gestione di classi dalla classe di base aggiungendo un gestore di classi che contrassegna l'evento indirizzato come gestito. È opportuno prestare particolare attenzione a questo approccio, poiché modificherà potenzialmente la progettazione dei controlli di base desiderata in aree quali l'aspetto visivo, la logica di stato, la gestione dell'input e dei comandi.

Gestione di classi di eventi indirizzati tramite le classi di base dei controlli

Su ogni nodo elemento specificato nella route di un evento, i listener di classi hanno la possibilità di rispondere all'evento indirizzato prima di qualsiasi listener di istanze dell'elemento. Per questo motivo, in alcuni casi i gestori di classi vengono utilizzati per eliminare eventi indirizzati che non devono essere propagati ulteriormente da una determinata implementazione di classi di controlli oppure per garantire una gestione speciale dell'evento indirizzato che rappresenta una funzionalità della classe. Ad esempio, una classe potrebbe generare un proprio evento specifico della classe che contiene più specifiche sul significato di alcune condizioni di input dell'utente nel contesto di tale classe specifica. L'implementazione della classe potrebbe successivamente contrassegnare l'evento indirizzato più generale come gestito. I gestori di classi vengono generalmente aggiunti in modo che non vengano richiamati per gli eventi indirizzati in cui i dati di evento condivisi sono già stati contrassegnati come gestiti, ma per i casi atipici è anche disponibile una firma RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) che registra i gestori di classi da richiamare anche quando gli eventi indirizzati sono contrassegnati come gestiti.

Elementi virtuali dei gestori di classi

Alcuni elementi, in particolare gli elementi di base come UIElement, espongono metodi virtuali "On*Event" e "OnPreview*Event" vuoti che corrispondono al relativo elenco di eventi indirizzati pubblici. È possibile eseguire l'override di questi metodi virtuali per implementare un gestore di classi per l'evento indirizzato. Le classi di elementi di base registrano questi metodi virtuali come gestore di classi per ogni evento indirizzato di questo tipo utilizzando RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) come descritto in precedenza. I metodi virtuali On*Event rendono notevolmente più semplice l'implementazione della gestione di classi per gli eventi indirizzati rilevanti, senza richiedere un'inizializzazione speciale nei costruttori statici per ogni tipo. Ad esempio, è possibile aggiungere la gestione di classi per l'evento DragEnter in qualsiasi classe derivata UIElement eseguendo l'override del metodo virtuale OnDragEnter. Nell'override, è possibile gestire l'evento indirizzato, generare altri eventi, avviare una logica specifica della classe che può modificare le proprietà degli elementi nelle istanze o qualsiasi combinazione di tali azioni. In genere, è necessario chiamare l'implementazione di base in tali override anche se si contrassegna l'evento come gestito. La chiamata all'implementazione di base è fortemente consigliata poiché il metodo virtuale si trova sulla classe di base. Il modello virtuale protetto standard delle chiamate alle implementazioni di base da ciascun elemento virtuale sostanzialmente sostituisce e affianca un meccanismo simile nativo della gestione di classi di eventi indirizzati, in base al quale i gestori di classi per tutte le classi in una gerarchia di classi sono chiamati su qualsiasi istanza specificata, a partire dal gestore di classi maggiormente derivato fino al gestore di classi di base. È necessario omettere la chiamata all'implementazione di base solo se la classe presenta un requisito intenzionale per modificare la classe di base che gestisce la logica. La possibilità di chiamare l'implementazione di base prima o dopo il codice di override dipende dalla natura dell'implementazione.

Gestione di classi di eventi di input

I metodi virtuali dei gestori di classi sono tutti registrati in modo che vengano richiamati solo nei casi in cui alcuni dati di evento condivisi non siano già contrassegnati come gestiti. Inoltre, solo per gli eventi di input, le versioni di tunneling e di bubbling vengono di solito generate in sequenza e condividono dati di evento. Questa condizione implica che per una determinata coppia di gestori di classi di eventi di input in cui uno è la versione di tunneling e l'altro quella di bubbling, non è necessario contrassegnare immediatamente l'evento come gestito. Se si implementa il metodo virtuale di gestione di classi di tunneling per contrassegnare l'evento come gestito, si impedisce al gestore di classi di bubbling di essere richiamato (oltre a impedire la chiamata a qualsiasi gestore di istanze normalmente registrato per l'evento di tunneling o di bubbling).

Dopo aver completato la gestione di classi in un nodo, vengono considerati i listener di istanze.

Aggiunta di gestori di istanze generati anche quando gli eventi sono contrassegnati come gestiti

Il metodo AddHandler fornisce un particolare overload che consente di aggiungere gestori che verranno richiamati dal sistema di eventi ogni volta che un evento raggiunge l'elemento di gestione nella route, anche se altri gestori hanno già adattato i dati di evento per contrassegnare l'evento come gestito. Questa operazione non viene in genere eseguita. Di solito, è possibile scrivere i gestori per adattare tutte le aree del codice dell'applicazione che potrebbero essere influenzate da un evento, indipendentemente dalla posizione in cui è stato gestito nella struttura ad albero dell'elemento, anche se si desidera ottenere più risultati finali. Inoltre, esiste in genere un solo elemento che deve rispondere a tale evento e la logica dell'applicazione appropriata si è già verificata. L'overload handledEventsToo, tuttavia, è disponibile per i casi eccezionali in cui qualche altro elemento nella struttura ad albero di un elemento o nella composizione dei controlli ha già contrassegnato un evento come gestito, ma è ancora necessario richiamare i gestori per altri elementi di livello superiore o inferiore nella struttura ad albero dell'elemento (a seconda della route).

Quando contrassegnare gli eventi gestiti come non gestiti

In genere, gli eventi indirizzati contrassegnati come gestiti non devono essere contrassegnati come non gestiti (Handled reimpostato su false) anche da gestori che operano su handledEventsToo. Tuttavia, alcuni eventi di input hanno rappresentazioni di eventi di alto e di basso livello che possono sovrapporrsi quando l'evento di alto livello viene visualizzato in una posizione nella struttura ad albero e l'evento di basso livello in un'altra posizione. Ad esempio, si consideri il caso in cui un elemento figlio rimane in ascolto di un evento tasto di alto livello, ad esempio TextInput, mentre un elemento padre rimane in ascolto di un evento di basso livello, ad esempio KeyDown. Se l'elemento padre gestisce l'evento di basso livello, l'evento di livello superiore può essere eliminato anche nell'elemento figlio che dovrebbe avere per primo l'opportunità di gestire l'evento.

In queste situazioni può essere necessario aggiungere gestori sia agli elementi padre sia agli elementi figlio per l'evento di basso livello. L'implementazione del gestore dell'elemento figlio può contrassegnare l'evento di basso livello come gestito, ma l'implementazione del gestore dell'elemento padre lo imposterebbe nuovamente come non gestito, così che ulteriori elementi superiori nella struttura ad albero (come l'evento di alto livello) abbiano l'opportunità di rispondere. Si tratta di una situazione piuttosto rara.

Eliminazione intenzionale degli eventi di input per la composizione dei controlli

Lo scenario principale in cui viene utilizzata la gestione di classi di eventi indirizzati è costituito dagli eventi di input e dai controlli compositi. Per definizione, un controllo composito è composto da più controlli o classi di base di controlli. Spesso l'autore del controllo desidera amalgamare tutti i possibili eventi di input che possono essere generati da ognuno dei sottocomponenti, per segnalare l'intero controllo come singola origine eventi. In alcuni casi, l'autore dei controlli può avere l'esigenza di eliminare completamente gli eventi dai componenti oppure di sostituire un evento definito dal componente che dispone di più informazioni o implica un comportamento più specifico. L'esempio canonico immediatamente visibile a qualsiasi autore di componenti è costituito dalla modalità in cui un oggetto Button Windows Presentation Foundation (WPF) gestisce qualsiasi evento del mouse che verrà risolto nell'evento intuitivo presente in tutti i pulsanti, un evento Click.

La classe di base Button (ButtonBase) deriva da Control che a sua volta deriva da FrameworkElement e UIElement, mentre la maggior parte dell'infrastruttura degli eventi necessaria per l'elaborazione dell'input del controllo è disponibile al livello di UIElement. In particolare, UIElement elabora eventi Mouse generali che gestiscono l'hit testing per il cursore del mouse all'interno dei relativi limiti e fornisce eventi distinti per le azioni più comuni dei pulsanti, ad esempio MouseLeftButtonDown. UIElement fornisce inoltre un oggetto OnMouseLeftButtonDown virtuale vuoto come gestore di classi preregistrato per MouseLeftButtonDown e ButtonBase ne esegue l'override. Analogamente, ButtonBase utilizza gestori di classi per MouseLeftButtonUp. Negli override, a cui vengono passati i dati di evento, le implementazioni contrassegnano l'istanza di RoutedEventArgs come gestita impostando Handled su true e quegli stessi dati di evento continuano nella parte restante della route ad altri gestori di classi e anche a gestori di istanze o metodi di impostazione degli eventi. Inoltre, l'override di OnMouseLeftButtonUp successivamente genererà l'evento Click. Il risultato finale per la maggior parte dei listener sarà la scomparsa degli eventi MouseLeftButtonDown e MouseLeftButtonUp e la relativa sostituzione con Click, un evento più significativo poiché è noto che ha avuto origine da un pulsante effettivo, non da parti composite del pulsante oppure da altri elementi.

Soluzione per l'eliminazione degli eventi da parte dei controlli

In alcuni casi il comportamento di eliminazione degli eventi all'interno di singoli controlli può interferire con alcune intenzioni più generali della logica di gestione degli eventi per l'applicazione. Ad esempio, se per qualche motivo l'applicazione presentava un gestore per MouseLeftButtonDown posizionato in corrispondenza dell'elemento radice dell'applicazione, è possibile notare che qualsiasi clic del mouse su un pulsante non richiama gestori MouseLeftButtonDown o MouseLeftButtonUp al livello radice. L'evento stesso in realtà è stato propagato (le route degli eventi non vengono effettivamente completate, ma il sistema dell'evento indirizzato modifica il comportamento di chiamata del gestore dopo essere stato contrassegnato come gestito). Quando l'evento indirizzato ha raggiunto il pulsante, la gestione della classe ButtonBase ha contrassegnato MouseLeftButtonDown come gestito per sostituire l'evento Click con più significato. Pertanto, qualsiasi gestore MouseLeftButtonDown standard più avanzato nella route non verrà richiamato. Esistono due tecniche che è possibile utilizzare per accertarsi che i gestori vengano richiamati in questa circostanza.

La prima consiste nell'aggiunta intenzionale del gestore utilizzando la firma handledEventsToo di AddHandler(RoutedEvent, Delegate, Boolean). Il limite di questo approccio è che questa tecnica di associazione di un gestore eventi è possibile solo dal codice, non dal markup. La semplice sintassi della specifica del nome del gestore eventi come un valore di attributo dell'evento tramite Extensible Application Markup Language (XAML) non abilita questo comportamento.

La seconda tecnica funziona solo per gli eventi di input, in cui le versioni di tunneling e di bubbling dell'evento indirizzato sono abbinate. Per questi eventi indirizzati, è invece possibile aggiungere gestori all'evento indirizzato equivalente di anteprima, o tunneling. Questo evento indirizzato effettua il tunneling nella route a partire dalla radice, di conseguenza il codice di gestione della classe del pulsante non lo intercetta, presupponendo che il gestore di anteprima sia stato associato al livello di un elemento predecessore nella struttura ad albero dell'elemento dell'applicazione. Se si utilizza questo approccio, è necessario prestare attenzione nel contrassegnare qualsiasi evento di anteprima come gestito. Nell'esempio riportato con PreviewMouseLeftButtonDown gestito in corrispondenza dell'elemento radice, se l'evento viene contrassegnato come Handled nell'implementazione del gestore, l'evento Click verrà in effetti eliminato. In genere questo comportamento non è appropriato.

Vedere anche

Attività

Procedura: creare un evento indirizzato personalizzato

Riferimenti

EventManager

Concetti

Eventi di anteprima

Cenni preliminari sugli eventi indirizzati