Strutture ad albero 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 degli oggetti 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. In genere gli sviluppatori WPF possono creare un'applicazione nel codice o definire parti dell'applicazione in XAML usando come riferimento concettuale la metafora della struttura ad albero di oggetti, ma chiameranno un'API specifica o useranno un markup specifico a tale scopo anziché un'API di modifica della struttura ad albero di oggetti generica simile a quella usata nel modello DOM XML. WPF espone due classi helper che forniscono una visualizzazione LogicalTreeHelper metafora albero e VisualTreeHelper. Nella documentazione di WPF vengono inoltre usati i termini struttura ad albero visuale e albero logico, in quanto tali strutture ad albero sono utili per la comprensione del comportamento di alcune funzionalità chiave 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 e introduce LogicalTreeHelper e VisualTreeHelper.

Strutture ad albero 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 a come si assegnano i valori 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 sempre si modifica direttamente l'albero logico o la struttura ad albero visuale, la corretta comprensione dei concetti correlati all'interazione delle strutture ad albero è utile per comprendere WPF in quanto 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é la struttura ad albero di oggetti è più un concetto che un'API effettiva, è possibile pensare a tale concetto anche come a un oggetto grafico. In pratica, in fase di esecuzione ci sono relazioni tra gli oggetti per cui la metafora di struttura ad albero non è valida. Ciononostante, in particolare con l'interfaccia utente definita in XAML, la metafora della struttura ad albero è sufficientemente pertinente da far sì che nella maggior parte della documentazione di WPF venga usato il termine struttura ad albero di oggetti per fare riferimento a tale concetto generale.

Albero logico

In WPF si aggiunge contenuto agli elementi dell'interfaccia utente impostando le proprietà degli oggetti che ritrasmetteno tali elementi. Ad esempio, si aggiungono elementi a un ListBox controllo modificandone la Items proprietà. In questo modo, si inseriscono elementi nell'oggetto ItemCollection che rappresenta il valore della Items proprietà. Analogamente, per aggiungere oggetti a un oggetto DockPanel, si modifica il relativo Children valore della proprietà. In questo caso si aggiungono oggetti all'oggetto 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 controllo o o in altri elementi dell'interfaccia utente in un DockPanel, si usano anche le Items proprietà 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 elaborasse questo codice XAML come XML in un modello DOM (Document Object Model) e fossero stati inclusi tag impostati come commenti impliciti (operazione consentita), la struttura ad albero DOM XML risultante includerebbe elementi per <ListBox.Items> e altri elementi impliciti. Poiché tuttavia XAML non elabora in questo modo durante la lettura del markup e la scrittura negli oggetti, l'oggetto grafico risultante non includerà letteralmente ListBox.Items. Dispone tuttavia di una proprietà denominata Items che contiene un ItemCollectionoggetto , che ItemCollection viene inizializzato ma vuoto quando viene elaborato XAMLListBox.ListBox Quindi, ogni elemento oggetto figlio esistente come contenuto per viene ListBox aggiunto al ItemCollection parser chiama a ItemCollection.Add. Fino a questo punto, in questo esempio di elaborazione di XAML in una struttura ad albero di oggetti la struttura ad albero di oggetti creata sembra fondamentalmente essere un albero logico.

Tuttavia, l'albero logico non è l'intero grafico a oggetti esistente per l'interfaccia utente dell'applicazione in fase di esecuzione, anche con gli elementi della sintassi implicita XAML inseriti in fattori. Il motivo principale di questo è oggetti visivi e modelli. Si consideri ad esempio .Button L'albero logico segnala l'oggetto Button e anche la relativa stringa Content. Nella struttura ad albero di oggetti di runtime, tuttavia, questo pulsante è molto più complesso. In particolare, il pulsante viene visualizzato solo sullo schermo perché è stato applicato un modello di controllo specifico Button . Gli oggetti visivi provenienti da un modello applicato (ad esempio il modello definito dal Border 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 del modello, è invece necessario esaminare la struttura ad albero visuale.

Per altre informazioni sul mapping della sintassi XAML al grafico a oggetti creato e sulla sintassi implicita in XAML, vedi Sintassi XAML in dettaglio o XAML in WPF.

Scopo dell'albero logico

L'albero logico consente ai modelli di contenuto di scorrere rapidamente i relativi oggetti figlio possibili e rende i modelli di contenuto estendibili. L'albero logico fornisce inoltre un framework per determinate notifiche, ad esempio relative al caricamento di tutti gli oggetti nell'albero logico stesso. Fondamentalmente l'albero logico è un'approssimazione di un oggetto grafico di runtime a livello di framework, che esclude gli oggetti visivi, ma è efficace per molte operazioni di query sulla composizione dell'applicazione di runtime.

Inoltre, i riferimenti a risorse statiche e dinamiche vengono risolti guardando verso l'alto attraverso l'albero logico per Resources le raccolte sull'oggetto richiedente iniziale e quindi continuando l'albero logico e controllando ogni FrameworkElement (o FrameworkContentElement) per un altro Resources valore che contiene , ResourceDictionarypossibilmente contenente tale chiave. L'albero logico viene usato per la ricerca delle risorse, quando sono presenti sia l'albero logico, sia la struttura ad albero visuale. Per altre informazioni sui dizionari risorse e sulla 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, l'albero LogicalTreeHelper logico talvolta contiene nodi che non FrameworkElement sono o FrameworkContentElement. Ad esempio, l'albero logico segnala il Text valore di un TextBlockoggetto , che è una stringa.

Override 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 con modalità tenda l aggiunge o rimuove oggetti all'interno dell'albero logico. Per un esempio relativo all'override dell'albero logico, vedere Eseguire l'override dell'albero logico.

Ereditarietà del valore della proprietà

L'ereditarietà dei valori delle proprietà funziona tramite un albero ibrido. I metadati effettivi che contengono la proprietà che abilita l'ereditarietà Inherits delle proprietà sono la classe a livello FrameworkPropertyMetadata di framework WPF. Pertanto, sia l'elemento padre che contiene il valore originale che l'oggetto figlio che eredita tale valore deve essere FrameworkElement o FrameworkContentElemente devono entrambi far parte di un albero logico. Per le proprietà WPF esistenti che supportano l'ereditarietà delle proprietà, tuttavia, l'ereditarietà dei valori delle proprietà può essere mantenuta tramite un nuovo oggetto non incluso nell'albero logico. Questa caratteristica è pertinente per lo più se si vuole fare in modo che gli elementi del modello usino valori di proprietà ereditati impostati sull'istanza basata sul modello o a livelli ancora superiori di composizione a livello di pagina e quindi più in alto nell'albero logico. Per garantire un funzionamento coerente dell'ereditarietà dei valori delle proprietà oltre tale limite, la proprietà che eredita deve essere registrata come proprietà associata. È consigliabile seguire questo modello anche se si vuole definire una proprietà di dipendenza personalizzata con un comportamento di ereditarietà della proprietà. L'albero esatto usato per l'ereditarietà delle proprietà non può essere completamente previsto da un metodo di utilità di una classe helper, nemmeno 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 Visual classe di base. Quando si scrive un modello per un controllo, si definisce o ridefinisce la struttura ad albero visuale relativa a quel controllo. La struttura ad albero visuale è di interesse anche per gli sviluppatori che vogliono un controllo di livello inferiore sul disegno per ragioni 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 visibile, a meno che l'utente non sia un autore di controlli. Il routing di eventi nella struttura ad albero visuale consente ai controlli che implementano la composizione a livello visivo di gestire 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 e non ereditano da Visual e non hanno una rappresentazione visiva. Per poter essere visualizzati in un'interfaccia utente, è necessario che sia ContentElement ospitato in un host di contenuto che sia un partecipante di un Visual albero logico. In genere un oggetto di questo tipo è un FrameworkElementoggetto . È possibile pensare all'host di contenuto come a un "browser" per il contenuto, che sceglie come visualizzare tale contenuto all'interno dell'area dello schermo controllata dall'host. Quando il contenuto è ospitato, può partecipare ad alcuni processi dell'albero che normalmente sono associati alla struttura ad albero visuale. In genere, la FrameworkElement classe host include codice di implementazione che aggiunge qualsiasi ospitato ContentElement alla route dell'evento tramite sottonodi dell'albero logico del contenuto, anche se il contenuto ospitato non fa parte della vera struttura ad albero visuale. Questa operazione è necessaria in modo che un oggetto ContentElement possa originare un evento indirizzato che instrada a qualsiasi elemento diverso da se stesso.

Attraversamento dell'albero

La LogicalTreeHelper classe fornisce i metodi , GetParente FindLogicalNode per l'attraversamento GetChildrendell'albero logico. Nella maggior parte dei casi, non è necessario attraversare l'albero logico dei controlli esistenti, perché i controlli espongono quasi sempre i relativi elementi figlio logici come proprietà di raccolta dedicata che supporta l'accesso alla raccolta, 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 previsti, ad ItemsControl esempio o Panel dove sono già definite le proprietà della raccolta e che intendono fornire il proprio supporto 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 VisualTreeHelper classe è il modo consigliato per attraversare la struttura ad albero visuale, se necessario per lo scenario di programmazione. Per altre informazioni, vedere Cenni preliminari sul rendering della grafica WPF.

Nota

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

Route per eventi indirizzati come "albero"

Come indicato in precedenza, la route di qualsiasi evento indirizzato percorre un singolo percorso predeterminato di un albero che consiste in una forma ibrida delle rappresentazioni di struttura ad albero visuale e albero logico. La route degli eventi può percorrere l'albero procedendo verso l'alto o verso il basso, a seconda che si tratti di un evento indirizzato di bubbling o di tunneling. Il concetto di route dell'evento non prevede una classe helper di supporto diretto, che potrebbe essere usata per "percorrere" la route dell'evento indipendentemente dalla generazione di un evento che effettivamente percorre una route. Esiste una classe che rappresenta la route, EventRoute, ma i metodi di tale classe sono in genere solo per uso interno.

Dizionari risorse e alberi

La ricerca nei dizionari risorse di tutti gli oggetti Resources definiti in una pagina attraversa fondamentalmente l'albero logico. Gli oggetti non inclusi nell'albero logico possono fare riferimento a risorse con chiave, ma la sequenza di ricerca delle risorse inizia nel punto in cui l'oggetto è connesso all'albero logico. In WPF, solo i nodi della struttura ad albero logica possono avere una Resources proprietà che contiene un ResourceDictionaryoggetto , pertanto non esiste alcun vantaggio nell'attraversare la struttura ad albero visuale alla ricerca di risorse con chiave da un oggetto ResourceDictionary.

La ricerca delle risorse può però estendersi anche oltre l'albero logico diretto. Per il markup dell'applicazione, la ricerca delle risorse può proseguire verso l'altro nei dizionari risorse a livello di applicazione e quindi verso il supporto dei temi e i valori di sistema a cui viene fatto riferimento come chiavi o proprietà statiche. Se i riferimenti alle risorse sono dinamici, i temi stessi possono fare riferimento anche ai valori di sistema esterni all'albero logico del tema. Per altre informazioni sui dizionari risorse e sulla logica di ricerca, vedere Risorse XAML.

Vedi anche