Ambiti dei nomi XAML

Un ambito dei nomi XAML archivia le relazioni tra i nomi degli oggetti definiti da XAML e i relativi equivalenti di istanza. Questo concetto è simile al significato più ampio del termine ambito dei nomi in altri linguaggi e tecnologie di programmazione.

Modalità di definizione degli ambiti dei nomi XAML

I nomi negli ambiti dei nomi XAML consentono al codice utente di fare riferimento agli oggetti inizialmente dichiarati in XAML. Il risultato interno dell'analisi di XAML è che il runtime crea un set di oggetti che conservano alcune o tutte le relazioni che questi oggetti avevano nelle dichiarazioni XAML. Queste relazioni vengono mantenute come proprietà dell'oggetto specifiche degli oggetti creati o vengono esposte ai metodi di utility nelle API del modello di programmazione.

L'uso più tipico di un nome in un ambito dei nomi XAML è un riferimento diretto a un'istanza di oggetto, che è abilitata dal passaggio di compilazione del markup come azione di compilazione del progetto, combinato con un metodo InitializeComponent generato nei modelli di classe parziale.

È possibile anche usare il metodo di utility FindName in fase di esecuzione per restituire un riferimento agli oggetti definiti con un nome nel markup XAML.

Altre informazioni su azioni di compilazione e XAML

Ciò che accade tecnicamente è che il codice XAML stesso subisce un passaggio del compilatore di markup contemporaneamente mentre XAML e la classe parziale che definisce per code-behind vengono compilati insieme. Ogni elemento oggetto con un attributo Nome o x:Name definito nel markup genera un campo interno con un nome corrispondente al nome XAML. Questo campo è inizialmente vuoto. La classe genera quindi un metodo InitializeComponent chiamato solo dopo il caricamento di tutto il codice XAML. All'interno della logica InitializeComponent logic, ogni campo interno viene quindi popolato con il valore restituito FindName per la stringa con nome equivalente. È possibile osservare questa infrastruttura manualmente esaminando i file ".g" (generati) creati per ogni pagina XAML nella sottocartella /obj di un progetto di app Windows Runtime dopo la compilazione. È anche possibile visualizzare i campi e il metodo InitializeComponent come membri dei gruppi risultanti se si riflette su di essi o esaminare in altro modo il relativo contenuto del linguaggio di interfaccia.

Note Specificamente per le app (C++/CX) con estensione del componente Visual C++, per l'elemento radice di un file XAML non viene creato un campo sottostante per un riferimento x:Name. Se è necessario fare riferimento all'oggetto radice da code-behind C++/CX, usare altre API o l'attraversamento albero. Ad esempio, è possibile chiamare FindName per un elemento figlio denominato e quindi chiamare Parent.

Creazione di oggetti in fase di esecuzione con XamlReader.Load

XAML può essere usato anche come input stringa per il metodo XamlReader.Load, che agisce in modo analogo all'operazione iniziale di analisi dell'origine XAML. XamlReader.Load crea un nuovo albero disconnesso di oggetti in fase di esecuzione. L'albero disconnesso può quindi essere collegato ad un certo punto dell'albero dell'oggetto principale. È necessario collegare in modo esplicito l'albero degli oggetti creato aggiungendolo a una raccolta di proprietà del contenuto, ad esempio Children, o impostando un'altra proprietà che accetta un valore dell'oggetto (ad esempio caricando un nuovo oggetto ImageBrush per un valore della proprietà Fill).

Implicazioni dell'ambito dei nomi XAML di XamlReader.Load

L'ambito dei nomi XAML preliminare definito dal nuovo albero degli oggetti creato da XamlReader.Load valuta l'univocità di tutti i nomi definiti nel codice XAML fornito. Se i nomi nel codice XAML specificato non sono univoci internamente a questo punto, XamlReader.Load genera un'eccezione. L'albero degli oggetti disconnesso non tenta di unire il relativo ambito dei nomi XAML con l'ambito dei nomi XAML principale dell'applicazione, se o quando è connesso all'albero degli oggetti dell'applicazione principale. Dopo aver connesso gli alberi, l'app ha un albero di oggetti unificato, ma tale albero include ambiti dei nomi XAML discreti. Le divisioni si verificano nei punti di connessione tra gli oggetti, in cui si imposta una proprietà come valore restituito da un richiamo di XamlReader.Load.

La complicazione di avere ambiti dei nomi XAML discreti e disconnessi è che i richiami al metodo FindName e i riferimenti diretti agli oggetti gestiti non operano più su un ambito dei nomi XAML unificato. Al contrario, l'oggetto specifico su cui viene chiamato FindName implica l'ambito, con l'ambito in cui si trova l'ambito dei nomi XAML in cui si trova l'oggetto chiamante. Nel caso di riferimento diretto dell'oggetto gestito, l'ambito è implicito nella classe in cui è presente il codice. In genere, il code-behind per l'interazione in fase di esecuzione di una "pagina" di contenuto dell'app è presente nella classe parziale che esegue il backup della "pagina" radice e pertanto l'ambito dei nomi XAML è l'ambito dei nomi XAML radice.

Se si chiama FindName per ottenere un oggetto denominato nell'ambito dei nomi XAML radice, il metodo non troverà gli oggetti da un ambito dei nomi XAML discreto creato da XamlReader.Load. Viceversa, se si chiama FindName da un oggetto ottenuto dall'ambito dei nomi XAML discreti, il metodo non troverà oggetti denominati nell'ambito dei nomi XAML radice.

Questo problema di ambito dei nomi XAML discreto influisce solo sulla ricerca di oggetti in base al nome negli ambiti dei nomi XAML quando si usa il richiamo di FindName.

Per ottenere riferimenti agli oggetti definiti in un ambito dei nomi XAML diverso, è possibile usare diverse tecniche:

  • Percorrere l'intero albero in passaggi discreti con proprietà Parent e/o di raccolta note che esistono nella struttura ad albero di oggetti, ad esempio l'insieme restituito da Panel.Children).
  • Se si sta chiamando da un ambito dei nomi XAML discreto e si vuole l'ambito dei nomi XAML radice, sia sempre facile ottenere un riferimento alla finestra principale attualmente visualizzata. È possibile ottenere la radice visiva (l'elemento XAML radice, noto anche come origine del contenuto) dalla finestra dell'applicazione corrente in una riga di codice con il richiamo Window.Current.Content. È quindi possibile eseguire il cast in FrameworkElement e richiamare FindName da questo ambito.
  • Se si sta chiamando dall'ambito dei nomi XAML radice e si desidera un oggetto all'interno di un ambito dei nomi XAML discreto, la cosa migliore da fare è pianificare in anticipo nel codice e conservare un riferimento all'oggetto restituito da XamlReader.Load e quindi aggiunto all'albero degli oggetti principale. Questo oggetto è ora un oggetto valido per richiamare FindName nell'ambito dei nomi XAML discreti. È possibile mantenere questo oggetto disponibile come variabile globale o passarlo in altro modo usando i parametri del metodo.
  • È possibile evitare completamente i nomi e le considerazioni relative all'ambito dei nomi XAML esaminando la struttura ad albero visuale. L'API VisualTreeHelper consente di attraversare l'albero visivo in termini di oggetti padre e raccolte figlio, in base esclusivamente alla posizione e all'indice.

Ambiti dei nomi XAML nei modelli

I modelli in XAML consentono di riutilizzare e riapplicare il contenuto in modo semplice, ma i modelli possono includere anche elementi con nomi definiti a livello di modello. Questo stesso modello potrebbe essere usato più volte in una pagina. Per questo motivo, i modelli definiscono i propri ambiti dei nomi XAML, indipendentemente dalla pagina contenitore in cui viene applicato lo stile o il modello. Si consideri questo esempio:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  >
  <Page.Resources>
    <ControlTemplate x:Key="MyTemplate">
      ....
      <TextBlock x:Name="MyTextBlock" />
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <SomeControl Template="{StaticResource MyTemplate}" />
    <SomeControl Template="{StaticResource MyTemplate}" />
  </StackPanel>
</Page>

Qui viene applicato lo stesso modello a due diversi controlli. Se i modelli non avessero ambiti dei nomi XAML discreti, il nome "MyTextBlock" usato nel modello causerebbe un conflitto di nomi. Poiché la creazione di ogni istanza del modello ha un ambito dei nomi XAML proprio, in questo esempio l'ambito dei nomi di ogni modello di cui è stata creata un'istanza contiene esattamente un solo nome. Tuttavia, l'ambito dei nomi XAML radice non contiene il nome di uno dei due modelli.

A causa degli ambiti dei nomi XAML separati, la ricerca di elementi denominati all'interno di un modello dall'ambito della pagina in cui viene applicato il modello richiede una tecnica diversa. Invece di richiamare FindName su un oggetto nell'albero degli oggetti, è prima di tutto possibile ottenere l'oggetto con il modello applicato e quindi richiamare GetTemplateChild. Un autore di un controllo che genera una convenzione in cui un determinato elemento denominato in un modello applicato è la destinazione per un comportamento definito dal controllo stesso può usare il metodo GetTemplateChild dal codice di implementazione del controllo. Il metodo GetTemplateChild è protetto, pertanto solo l'autore del controllo può accedervi. Esistono inoltre convenzioni che gli autori di controlli devono seguire per denominare parti e parti del modello e segnalarli come valori di attributo applicati alla classe del controllo. Questa tecnica rende individuabili i nomi di parti importanti per controllare gli utenti che potrebbero voler applicare un modello diverso, che dovrà sostituire le parti denominate per mantenere la funzionalità del controllo.