Condividi tramite


Alberi in WPF

In molte tecnologie, gli elementi e i componenti sono organizzati in una struttura ad albero in cui gli sviluppatori modificano direttamente i nodi oggetto nell'albero per influire sul rendering o sul comportamento di un'applicazione. Windows Presentation Foundation (WPF) usa anche diverse metafore della struttura ad albero per definire le relazioni tra gli elementi del programma. Nella maggior parte dei casi, gli sviluppatori WPF possono creare un'applicazione nel codice o definire parti dell'applicazione in XAML, pensando concettualmente alla metafora dell'albero degli oggetti, ma chiameranno API specifiche o useranno markup specifico per farlo, invece di una generale API per la manipolazione dell'albero degli oggetti, come potreste usare in XML DOM. WPF espone due classi helper che forniscono una visualizzazione a forma di albero, LogicalTreeHelper e VisualTreeHelper. I termini albero visivo e albero logico vengono usati anche nella documentazione WPF perché questi stessi alberi sono utili per comprendere il comportamento di determinate funzionalità principali di WPF. Questo argomento definisce la rappresentazione dell'albero visivo e dell'albero logico, illustra in che modo tali alberi sono correlati a un concetto complessivo di albero degli oggetti e introduce LogicalTreeHelper e VisualTreeHelpers.

Alberi in WPF

La struttura ad albero più completa in WPF è l'albero degli oggetti. Se definisci una pagina dell'applicazione in XAML e quindi carichi il codice XAML, la struttura ad albero viene creata in base alle relazioni di annidamento degli elementi nel markup. Se si definisce un'applicazione o una parte dell'applicazione nel codice, la struttura ad albero viene creata in base alla modalità di assegnazione dei valori delle proprietà per le proprietà che implementano il modello di contenuto per un determinato oggetto. In WPF esistono due modi in cui la struttura ad albero degli oggetti completa viene concettualizzata e può essere segnalata all'API pubblica: come albero logico e come struttura ad albero visuale. Le distinzioni tra albero logico e albero visivo non sono sempre importanti, ma talvolta possono causare problemi con determinati sottosistemi WPF e influire sulle scelte effettuate nel markup o nel codice.

Anche se non si modifica sempre direttamente l'albero logico o la struttura ad albero visuale, comprendere i concetti di come interagiscono gli alberi è utile per comprendere WPF come tecnologia. Pensare a WPF come una metafora ad albero di qualche tipo è anche fondamentale per comprendere come funziona l'ereditarietà delle proprietà e il routing degli eventi in WPF.

Nota

Poiché l'albero degli oggetti è più un concetto che un'API effettiva, un altro modo per considerare il concetto è come un grafo di oggetti. In pratica, esistono relazioni tra oggetti in fase di esecuzione in cui la metafora dell'albero non reggerà. Tuttavia, in particolare con l'interfaccia utente definita da XAML, la metafora dell'albero è sufficientemente rilevante che la maggior parte della documentazione WPF userà il termine albero degli oggetti quando si fa riferimento a questo concetto generale.

Albero logico

In WPF si aggiunge contenuto agli elementi dell'interfaccia utente impostando le proprietà degli oggetti che supportano tali elementi. Ad esempio, si aggiungono elementi a un controllo ListBox modificandone la proprietà Items. Inserisci gli elementi nel ItemCollection, che corrisponde al valore della proprietà Items. Analogamente, per aggiungere oggetti a un DockPanel, si modifica il relativo valore della proprietà Children. In questo caso si aggiungono oggetti al UIElementCollection. Per un esempio di codice, vedere Procedura: Aggiungere un elemento in modo dinamico.

In Extensible Application Markup Language (XAML) quando si inserisce elementi di elenco in un ListBox o controlli o in altri elementi dell'interfaccia utente in un DockPanel, si usano anche le proprietà Items e Children, in modo esplicito o implicito, come nell'esempio seguente.

<DockPanel
  Name="ParentElement"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <!--implicit: <DockPanel.Children>-->
  <ListBox DockPanel.Dock="Top">
    <!--implicit: <ListBox.Items>-->
    <ListBoxItem>
      <TextBlock>Dog</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Cat</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Fish</TextBlock>
    </ListBoxItem>
  <!--implicit: </ListBox.Items>-->
  </ListBox>
  <Button Height="20" Width="100" DockPanel.Dock="Top">Buy a Pet</Button>
  <!--implicit: </DockPanel.Children>-->
</DockPanel>

Se si dovesse elaborare questo XAML come XML in un modello di oggetti del documento, e se avessi incluso i tag commentati come impliciti (il che sarebbe stato legale), allora l'albero DOM XML risultante avrebbe incluso elementi per <ListBox.Items> e gli altri elementi impliciti. Ma XAML non funziona in questo modo: quando si legge il markup e si scrive su oggetti, il grafico degli oggetti risultante non include letteralmente ListBox.Items. Ha tuttavia una proprietà ListBox chiamata Items che contiene un ItemCollection, e il ItemCollection viene inizializzato ma rimane vuoto quando il codice XAML ListBox viene elaborato. Ogni elemento oggetto figlio esistente come contenuto per il ListBox viene quindi aggiunto al ItemCollection grazie alle chiamate del parser verso ItemCollection.Add. Questo esempio di elaborazione di XAML in un albero di oggetti è finora apparentemente un esempio in cui l'albero degli oggetti creato è fondamentalmente l'albero logico.

Tuttavia, l'albero logico non è l'intero grafico degli oggetti esistente per l'interfaccia utente dell'applicazione in fase di esecuzione, anche escludendo gli elementi della sintassi implicita XAML. Il motivo principale di questo sono gli oggetti visivi e i modelli. Si consideri ad esempio il Button. L'albero logico segnala l'oggetto Button e anche la relativa stringa Content. Ma c'è di più riguardo a questo pulsante nell'albero degli oggetti di esecuzione. In particolare, il pulsante viene visualizzato sullo schermo solo perché è stato applicato un specifico modello di controllo Button. Gli oggetti visivi provenienti da un modello applicato ,ad esempio il modello definito dal modello Border di grigio scuro intorno al pulsante visivo, non vengono segnalati nell'albero logico, anche se si sta esaminando l'albero logico durante l'esecuzione ,ad esempio gestendo un evento di input dall'interfaccia utente visibile e quindi leggendo l'albero logico. Per trovare gli oggetti visivi modello, è invece necessario esaminare l'albero visivo.

Per ulteriori informazioni su come la sintassi XAML viene mappata al grafico degli oggetti creati e sulla sintassi implicita in XAML, vedere Sintassi XAML in Dettaglio o XAML in WPF.

Scopo dell'albero logico

L'albero logico esiste affinché i modelli di contenuto possano iterare facilmente sui possibili oggetti figlio e affinché i modelli di contenuto possano essere estensibili. Inoltre, l'albero logico fornisce un framework per determinate notifiche, ad esempio quando vengono caricati tutti gli oggetti nell'albero logico. Fondamentalmente, l'albero logico è un'approssimazione di un grafico di oggetti in fase di esecuzione a livello di framework, che esclude gli oggetti visivi, ma è adeguato per molte operazioni di query sulla struttura dell'applicazione in esecuzione.

Inoltre, i riferimenti a risorse statiche e dinamiche vengono risolti esaminando verso l'alto l'albero logico per le raccolte Resources sull'oggetto richiedente iniziale e quindi continuando l'albero logico e controllando ogni FrameworkElement (o FrameworkContentElement) per un altro valore Resources che contiene un ResourceDictionary, eventualmente contenente tale chiave. L'albero logico viene usato per la ricerca delle risorse quando sono presenti sia l'albero logico che la struttura ad albero visuale. Per ulteriori informazioni sui dizionari di risorse e ricerca, vedere Risorse XAML.

Composizione dell'albero logico

L'albero logico viene definito a livello di framework WPF, il che significa che l'elemento di base WPF più rilevante per le operazioni di albero logico è FrameworkElement o FrameworkContentElement. Tuttavia, come si può vedere se si usa effettivamente l'API LogicalTreeHelper, l'albero logico a volte contiene nodi che non sono né FrameworkElement o FrameworkContentElement. Ad esempio, l'albero logico segnala il valore Text di un TextBlock, ovvero una stringa.

Ridefinizione dell'albero logico

Gli autori di controlli avanzati possono eseguire l'override dell'albero logico eseguendo l'override di diverse API che definiscono il modo in cui un oggetto generale o un modello di contenuto aggiunge o rimuove oggetti all'interno dell'albero logico. Per un esempio di come eseguire l'override dell'albero logico, vedere Eseguire l'override dell'albero logico.

Ereditarietà del valore della proprietà

L'ereditarietà del valore della proprietà funziona tramite un albero ibrido. I metadati effettivi che contengono la proprietà Inherits che abilita l'ereditarietà delle proprietà sono la classe FrameworkPropertyMetadata a livello di framework WPF. Pertanto, sia l'elemento padre che contiene il valore originale che l'oggetto figlio che eredita tale valore devono essere entrambi FrameworkElement o FrameworkContentElemente devono entrambi far parte di un albero logico. Tuttavia, per le proprietà WPF esistenti che supportano l'ereditarietà delle proprietà, l'ereditarietà dei valori di proprietà è in grado di mantenersi tramite un oggetto intermedio che non si trova nell'albero logico. Principalmente questo è rilevante per la presenza di elementi modello che usano i valori delle proprietà ereditati impostati nell'istanza basata su modelli o a livelli ancora più elevati di composizione a livello di pagina e quindi superiore nell'albero logico. Affinché l'ereditarietà dei valori di proprietà funzioni in modo coerente attraverso tale limite, la proprietà che eredita deve essere registrata come proprietà associata ed è necessario seguire questo modello se si intende definire una proprietà di dipendenza personalizzata con comportamento di ereditarietà delle proprietà. L'albero esatto utilizzato per l'ereditarietà delle proprietà non può essere interamente previsto da un metodo di utilità della classe helper, anche in fase di esecuzione. Per altre informazioni, vedere ereditarietà del valore della proprietà .

Struttura ad albero visuale

Oltre al concetto di albero logico, esiste anche il concetto di struttura ad albero visuale in WPF. La struttura ad albero visuale descrive la struttura degli oggetti visivi, rappresentata dalla classe di base Visual. Quando si scrive un modello per un controllo, si sta definendo o ridefinendo la struttura ad albero visuale applicabile per tale controllo. La struttura ad albero visuale è di interesse anche per gli sviluppatori che desiderano un controllo di livello inferiore sul disegno per motivi di prestazioni e ottimizzazione. Un'esposizione della struttura ad albero visuale come parte della programmazione convenzionale dell'applicazione WPF è che le route di eventi per un evento indirizzato viaggiano principalmente lungo la struttura ad albero visuale, non l'albero logico. Questa sottigliezza del comportamento degli eventi indirizzati potrebbe non essere immediatamente evidente a meno che non si sia un progettista di controlli. Il routing degli eventi tramite l'albero visivo consente ai controlli che implementano la composizione a livello visivo di gestire gli eventi o creare setter di eventi.

Alberi, elementi di contenuto e host di contenuto

Gli elementi di contenuto (classi che derivano da ContentElement) non fanno parte della struttura ad albero visuale; non ereditano da Visual e non hanno una rappresentazione visiva. Per poter essere visualizzato in un'interfaccia utente, un ContentElement deve essere ospitato in un host di contenuto che sia allo stesso tempo un Visual e un partecipante dell'albero logico. In genere un oggetto di questo tipo è un FrameworkElement. È possibile concettualizzare che l'host del contenuto è un po ' come un "browser" per il contenuto e sceglie come visualizzare il contenuto all'interno dell'area dello schermo che l'host controlla. Quando il contenuto è ospitato, il contenuto può essere reso partecipante in determinati processi ad albero associati normalmente alla struttura ad albero visuale. Generalmente, la classe host FrameworkElement include codice di implementazione che aggiunge qualsiasi ContentElement ospitata al percorso dell'evento tramite sottonodi dell'albero logico del contenuto, anche se il contenuto ospitato non fa parte del vero albero visivo. Questa operazione è necessaria in modo che un ContentElement possa originare un evento indirizzato che instrada a qualsiasi elemento diverso da se stesso.

Attraversamento dell'albero

La classe LogicalTreeHelper fornisce i metodi GetChildren, GetParente FindLogicalNode per l'attraversamento dell'albero logico. Nella maggior parte dei casi, non è necessario percorrere l'albero logico dei controlli esistenti, perché questi controlli espongono quasi sempre i relativi elementi figlio logici come proprietà di raccolta dedicata che supporta l'accesso alla raccolta, come ad esempio Add, un indicizzatore e così via. L'attraversamento ad albero è principalmente uno scenario usato dagli autori di controlli che scelgono di non derivare da schemi di controllo designati, come ItemsControl o Panel, in cui le proprietà della raccolta sono già definite, e che intendono fornire un supporto personalizzato per le proprietà della raccolta.

La struttura ad albero visuale supporta anche una classe helper per l'attraversamento della struttura ad albero visuale, VisualTreeHelper. La struttura ad albero visuale non viene esposta in modo pratico tramite proprietà specifiche del controllo, pertanto la classe VisualTreeHelper è il modo consigliato per navigare l'albero visuale, se necessario per il tuo scenario di programmazione. Per altre informazioni, vedere Panoramica del rendering della grafica WPF .

Nota

A volte è necessario esaminare la struttura ad albero visuale di un modello applicato. Prestare attenzione quando si usa questa tecnica. Anche se si sta attraversando una struttura ad albero visuale per un controllo in cui si definisce il modello, i consumatori del controllo possono sempre modificare il modello impostando la proprietà Template sulle istanze, e anche l'utente finale può influenzare il modello applicato modificando il tema del sistema.

Route per gli eventi indirizzati come "albero"

Come accennato in precedenza, il percorso di un determinato evento indirizzato si sposta lungo un percorso singolo e predeterminato di un albero che è un ibrido delle rappresentazioni ad albero visive e logiche. Il percorso dell'evento può viaggiare nelle direzioni su o giù all'interno dell'albero a seconda che si tratti di un evento instradato o di tunneling. Il concetto di route di eventi non dispone di una classe helper di supporto diretta che può essere usata per "camminare" la route dell'evento indipendentemente dalla generazione di un evento che effettivamente instrada. Esiste una classe che rappresenta la route, EventRoute, ma i metodi di tale classe sono in genere solo per uso interno.

Dizionari delle risorse e alberi

La ricerca del dizionario delle risorse per tutte le Resources definite in una pagina scorre essenzialmente attraverso l'albero logico. Gli oggetti che non si trovano nell'albero logico possono fare riferimento a risorse con chiave, ma la sequenza di ricerca delle risorse inizia nel punto in cui tale oggetto è connesso all'albero logico. In WPF, solo i nodi della struttura ad albero logica possono avere una proprietà Resources che contiene un ResourceDictionary, pertanto non esiste alcun vantaggio nell'attraversare la struttura ad albero visuale cercando risorse con chiave da un ResourceDictionary.

Tuttavia, la ricerca delle risorse può estendersi anche oltre l'albero logico immediato. Per il markup dell'applicazione, la ricerca delle risorse può quindi continuare con i dizionari risorse a livello di applicazione e quindi al supporto del tema e ai valori di sistema, a cui si fa riferimento come proprietà statiche o chiavi. I temi stessi possono anche fare riferimento a valori di sistema all'esterno dell'albero logico del tema se i riferimenti alle risorse sono dinamici. Per ulteriori informazioni sui dizionari delle risorse e sulla logica di ricerca, vedere risorse di XAML.

Vedere anche