Panoramica di XAML (WPF .NET)

Questo articolo descrive le funzionalità del linguaggio XAML e ne illustra l'uso per la scrittura di app Windows Presentation Foundation (WPF). In particolare descrive il linguaggio XAML implementato in WPF. Il linguaggio XAML in quanto tale, tuttavia, rappresenta un concetto di linguaggio più ampio rispetto a quello implementato in WPF.

Importante

La documentazione di Desktop Guide per .NET 7 e .NET 6 è in fase di costruzione.

Che cos'è XAML

XAML è un linguaggio di markup dichiarativo. Come applicato al modello di programmazione .NET, XAML semplifica la creazione di un'interfaccia utente per un'app .NET. È possibile creare elementi dell'interfaccia utente visibili nel markup XAML dichiarativo, quindi separare la definizione dell'interfaccia utente dalla logica di runtime utilizzando file code-behind uniti al markup tramite definizioni di classe parziali. XAML rappresenta in modo diretto la creazione di istanze di oggetti in un set specifico di tipi di supporto definiti in assembly. In ciò si differenzia dalla maggior parte degli altri linguaggi di markup, che sono in genere linguaggi interpretati senza un legame diretto con il sistema di tipi di supporto. XAML consente un flusso di lavoro in cui parti distinte possono operare nell'interfaccia utente e nella logica di un'app, usando strumenti potenzialmente diversi.

Se rappresentati come testo, i file XAML sono file XML, in genere con estensione .xaml. I file possono essere codificati tramite qualsiasi codifica XML, tuttavia la codifica tipica è UTF-8.

L'esempio seguente illustra come creare un pulsante come parte di un'interfaccia utente. L'esempio non deve essere ritenuto esaustivo e ha il semplice scopo di dare un'idea molto generale del modo in cui XAML rappresenta metafore comuni di programmazione dell'interfaccia utente.

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Cenni sulla sintassi XAML

Le sezioni seguenti descrivono le forme di base della sintassi XAML e offrono un breve esempio di markup. L'obiettivo di queste sezioni non consiste nell'offrire informazioni complete su ogni forma di sintassi, ad esempio sulla modalità di rappresentazione di queste sintassi nel sistema di tipi di supporto. Per altre informazioni sulle specifiche della sintassi XAML, vedere Descrizione dettagliata della sintassi XAML.

Se si ha familiarità con il linguaggio XML, la maggior parte del materiale incluso in alcune delle sezioni successive risulterà di facile comprensione. Ciò è dovuto a uno dei principi di base della progettazione XAML. Il linguaggio XAML definisce concetti propri, tuttavia questi concetti funzionano nel linguaggio XML e nel formato del markup.

Elementi oggetto di XAML

In genere, un elemento oggetto dichiara un'istanza di un tipo. Il tipo è definito negli assembly cui viene fatto riferimento nella tecnologia che usa XAML come linguaggio.

La sintassi degli elementi oggetto inizia sempre con una parentesi angolare di apertura (<), seguita dal nome del tipo in cui si desidera creare un'istanza. Il nome può includere un prefisso. Questo concetto verrà illustrato in seguito. Se si desidera, è quindi possibile dichiarare attributi nell'elemento oggetto. Per completare il tag dell'elemento oggetto, terminare con una parentesi angolare di chiusura (>). In alternativa, è possibile usare una forma di chiusura automatica senza contenuto, completando il tag con una barra e una parentesi angolare di chiusura in successione (/>). Osservare nuovamente il frammento di markup illustrato in precedenza.

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

Vengono specificati due elementi oggetto: <StackPanel> (con contenuto e un tag di chiusura successivo) e <Button .../> (forma di chiusura automatica, con diversi attributi). Viene eseguito il mapping degli elementi oggetto StackPanel e Button al nome di una classe definita da WPF e che fa parte degli assembly WPF. Quando si specifica il tag di un elemento oggetto, si crea un'istruzione per la creazione di una nuova istanza del tipo sottostante da parte dell'elaborazione XAML. Ogni istanza viene creata chiamando il costruttore senza parametri del tipo sottostante durante l'analisi e il caricamento del markup XAML.

Sintassi per attributi (proprietà)

Spesso le proprietà di un oggetto possono essere espresse come attributi dell'elemento oggetto. La sintassi per attributi definisce un nome per la proprietà oggetto impostata, seguito dall'operatore di assegnazione (=). Il valore di un attributo viene sempre specificato come stringa inclusa tra virgolette.

La sintassi per attributi è la sintassi per l'impostazione di proprietà più semplice, nonché la più intuitiva per gli sviluppatori che hanno già usato linguaggi di markup in passato. Il markup seguente, ad esempio, crea un pulsante che presenta un testo rosso e uno sfondo blu oltre al testo visualizzato Content.

<Button Background="Blue" Foreground="Red" Content="This is a button"/>

Sintassi per gli elementi proprietà

Per alcune proprietà di un elemento oggetto, non si può usare la sintassi per attributi in quanto non è possibile esprimere l'oggetto o le informazioni necessarie per indicare il valore di proprietà in modo adeguato tra virgolette e nel rispetto delle limitazioni della stringa della sintassi per attributi. In questi casi, è possibile usare una sintassi diversa, nota come sintassi per elementi proprietà.

La sintassi per il tag di inizio dell'elemento proprietà è <TypeName.PropertyName>. Il contenuto di tale tag è in genere un elemento oggetto del tipo accettato come valore dalla proprietà. Dopo avere specificato il contenuto, è necessario chiudere l'elemento proprietà con un tag di fine. La sintassi per il tag di fine è </TypeName.PropertyName>.

Se supportata, la sintassi per attributi è in genere più efficace e consente un markup più compatto. Si tratta tuttavia di una questione di stile e non di un limite tecnico. L'esempio seguente mostra le stesse proprietà impostate nell'esempio di sintassi per attributi precedente, tuttavia questa volta viene usata la sintassi per elementi proprietà per tutte le proprietà dell'oggetto Button.

<Button>
    <Button.Background>
        <SolidColorBrush Color="Blue"/>
    </Button.Background>
    <Button.Foreground>
        <SolidColorBrush Color="Red"/>
    </Button.Foreground>
    <Button.Content>
        This is a button
    </Button.Content>
</Button>

Sintassi della raccolta

Il linguaggio XAML include alcune ottimizzazioni che producono un markup più leggibile per l'utente. Una di queste ottimizzazioni prevede che se una proprietà specifica accetta un tipo di raccolta, gli elementi dichiarati nel markup come elementi figlio in quel valore della proprietà diventano di conseguenza parte della raccolta. In questo caso, una raccolta di elementi oggetto figlio corrisponde al valore impostato nella proprietà della raccolta.

L'esempio seguente mostra la sintassi per raccolte per l'impostazione dei valori della proprietà GradientStops.

<LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
        <!-- no explicit new GradientStopCollection, parser knows how to find or create -->
        <GradientStop Offset="0.0" Color="Red" />
        <GradientStop Offset="1.0" Color="Blue" />
    </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

Proprietà del contenuto XAML

XAML specifica una funzionalità del linguaggio tramite cui una classe può designare esattamente una delle relative proprietà come proprietà di contenuto XAML. Per impostare il valore di questa proprietà di contenuto vengono usati gli elementi figlio. In altre parole, solo per la proprietà di contenuto, è possibile omettere un elemento proprietà quando si imposta tale proprietà nel markup XAML ed è possibile produrre una metafora padre/figlio più visibile nel markup.

Ad esempio, Border specifica una proprietà di contenuto di Child. I due elementi Border seguenti vengono gestiti in modo identico. Il primo sfrutta la sintassi della proprietà di contenuto e omette l'elemento proprietà Border.Child. Il secondo specifica Border.Child in modo esplicito.

<Border>
    <TextBox Width="300"/>
</Border>
<!--explicit equivalent-->
<Border>
    <Border.Child>
        <TextBox Width="300"/>
    </Border.Child>
</Border>

Secondo le regole del linguaggio XAML, il valore di una proprietà di contenuto XAML deve essere specificato completamente prima o completamente dopo qualsiasi altro elemento proprietà nell'elemento oggetto. Ad esempio, il markup seguente non viene compilato.

<Button>I am a
  <Button.Background>Blue</Button.Background>
  blue button</Button>

Per altre informazioni sulle specifiche della sintassi XAML, vedere Descrizione dettagliata della sintassi XAML.

Contenuto di testo

Un numero ridotto di elementi XAML può elaborare il testo direttamente come contenuto. A tale scopo, deve verificarsi uno dei casi seguenti:

  • La classe deve dichiarare una proprietà di contenuto che deve essere di un tipo assegnabile a una stringa (il tipo potrebbe essere Object). Ad esempio, qualsiasi elemento ContentControl usa Content come proprietà di contenuto ed è di tipo Object per supportare la sintassi seguente in un elemento ContentControl come Button: <Button>Hello</Button>.

  • Il tipo deve dichiarare un convertitore di tipi e, in tal caso, il contenuto di testo viene usato come testo di inizializzazione per il convertitore di tipi. Ad esempio, <Brush>Blue</Brush> converte il valore del contenuto di Blue in un pennello. Nella pratica questo caso è meno comune.

  • Il tipo deve essere un tipo primitivo noto del linguaggio XAML.

Combinazione di proprietà di contenuto e sintassi per raccolte

Si consideri l'esempio seguente.

<StackPanel>
    <Button>First Button</Button>
    <Button>Second Button</Button>
</StackPanel>

In questo caso, ogni elemento Button è un elemento figlio di StackPanel. Si tratta di un markup semplice e intuitivo nel quale vengono omessi due tag per due ragioni diverse.

  • Elemento proprietà StackPanel.Children omesso: StackPanel deriva da Panel. Panel definisce Panel.Children come relativa proprietà di contenuto XAML.

  • Elemento oggetto UIElementCollection omesso: la proprietà Panel.Children accetta il tipo UIElementCollection, che implementa IList. In base alle regole XAML per l'elaborazione di raccolte come IList, è possibile omettere il tag dell'elemento della raccolta. In questo caso, non è effettivamente possibile creare un'istanza di UIElementCollection in quanto non espone un costruttore senza parametri e per questo motivo l'elemento oggetto UIElementCollection risulta impostato come commento.

<StackPanel>
    <StackPanel.Children>
        <!--<UIElementCollection>-->
        <Button>First Button</Button>
        <Button>Second Button</Button>
        <!--</UIElementCollection>-->
    </StackPanel.Children>
</StackPanel>

Sintassi per attributi (eventi)

È possibile usare la sintassi per attributi anche per membri che sono eventi anziché proprietà. In questo caso il nome dell'attributo corrisponde al nome dell'evento. Nell'implementazione WPF di eventi per XAML il valore dell'attributo corrisponde al nome di un gestore che implementa il delegato dell'evento. Nel markup seguente, ad esempio, viene assegnato un gestore per l'evento Click a un controllo Button creato nel markup:

<Button Click="Button_Click" >Click Me!</Button>

La correlazione tra eventi e XAML in WPF è molto più complessa di questo semplice esempio di sintassi per attributi. Ci si potrebbe chiedere ad esempio, cosa rappresenti ClickHandler a cui viene fatto riferimento e come questo venga definito. Questi aspetti verranno trattati nella sezione Eventi e code-behind XAML più avanti in questo articolo.

Maiuscolo e minuscolo e spazi vuoti in XAML

In generale, nel linguaggio XML viene fatta distinzione tra maiuscole e minuscole. Per la risoluzione di tipi di supporto, XAML WPF fa distinzione tra maiuscole e minuscole in base alle stesse regole applicate da CLR relativamente a tale distinzione. Gli elementi oggetto, gli elementi proprietà e i nomi di attributo devono essere sempre specificati usando correttamente le maiuscole e le minuscole quando si esegue il confronto del nome con il nome del tipo sottostante nell'assembly o con il nome di un membro di un tipo. La distinzione tra maiuscole e minuscole vale anche per le parole chiave e le primitive del linguaggio XAML. Per i valori la distinzione tra maiuscole e minuscole non si applica sempre. Tale distinzione per i valori dipende dal comportamento del convertitore dei tipi associato alla proprietà che accetta il valore o al tipo di valore della proprietà. Ad esempio, le proprietà che accettano il tipo Boolean possono accettare true o True come valori equivalenti, ma solo perché la conversione in Boolean dei tipi nativi del parser XAML WPF per la stringa consente già tali valori come equivalenti.

I processori e i serializzatori XAML WPF ignoreranno o rimuoveranno tutti gli spazi vuoti non significativi e normalizzeranno gli spazi vuoti significativi. Questo è coerente con i requisiti di comportamento degli spazi vuoti predefiniti della specifica XAML. Questo comportamento è significativo solo quando si specificano stringhe all'interno di proprietà di contenuto XAML. In termini più semplici, in XAML i caratteri di spazio, avanzamento riga e tabulazione vengono convertiti in spazi e viene conservato solo l'eventuale spazio rilevato a una delle estremità di una stringa contigua. Questo articolo non include la spiegazione completa della gestione degli spazi vuoti in XAML. Per altre informazioni, vedere Elaborazione degli spazi vuoti in XAML.

Estensioni di markup

Le estensioni di markup sono un concetto del linguaggio XAML. Se usate per specificare il valore di una sintassi per attributi, le parentesi graffe ({ e }) indicano l'uso di un'estensione di markup. Questo uso indica all'elaborazione XAML che, diversamente dal solito, i valori degli attributi non devono essere considerati valori di stringa letterale o valori convertibili in stringa.

Le estensioni di markup maggiormente usate nella programmazione di app WPF sono Binding, usata per le espressioni di associazione dati, e i riferimenti alle risorse StaticResource e DynamicResource. Le estensioni di markup consentono di usare la sintassi per attributi per specificare valori per le proprietà anche quando queste non supportano in generale una sintassi per attributi. Spesso le estensioni di markup vengono usate con tipi di espressione intermedi per abilitare funzionalità quali il posticipo di valori o il riferimento ad altri oggetti presenti solo in fase di esecuzione.

Nel markup seguente, ad esempio, viene impostato il valore della proprietà Style usando la sintassi per attributi. La proprietà Style accetta un'istanza della classe Style di cui per impostazione predefinita non è stato possibile creare un'istanza tramite una stringa di sintassi per attributi. Tuttavia, in questo caso l'attributo fa riferimento a una particolare estensione di markup, ovvero StaticResource. Quando quell'estensione di markup viene elaborata, restituisce un riferimento a uno stile del quale in precedenza è stata creata un'istanza come risorsa con chiave in un dizionario di risorse.

<Window x:Class="index.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="100" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
        <Style TargetType="Border" x:Key="PageBackground">
            <Setter Property="BorderBrush" Value="Blue"/>
            <Setter Property="BorderThickness" Value="5" />
        </Style>
    </Window.Resources>
    <Border Style="{StaticResource PageBackground}">
        <StackPanel>
            <TextBlock Text="Hello" />
        </StackPanel>
    </Border>
</Window>

Per un elenco di riferimento di tutte le estensioni di markup per il linguaggio XAML implementato in modo specifico in WPF, vedere Estensioni XAML WPF. Per un elenco di riferimento delle estensioni di markup definite da System.Xaml e sono più ampiamente disponibili per le implementazioni XAML .NET, vedi Funzionalità del linguaggio dello spazio dei nomi XAML (x:). Per altre informazioni sulle estensioni di markup, vedere Estensioni di markup e WPF XAML.

Convertitori di tipi

La sezione Cenni sulla sintassi XAML afferma che il valore dell'attributo deve poter essere impostato tramite una stringa. La gestione nativa di base della conversione delle stringhe in altri tipi di oggetto o valori primitivi si basa sul tipo String stesso, nonché su un'elaborazione nativa per determinati tipi, ad esempio DateTime o Uri. Molti tipi WPF o membri di tali tipi, tuttavia, estendono il comportamento di elaborazione degli attributi stringa di base in modo che le istanze di tipi di oggetto più complessi possano essere specificate come stringhe e attributi.

La struttura Thickness è un esempio di un tipo associato a una conversione di tipi abilitata per gli usi di XAML. Thickness indica misure all'interno di un rettangolo annidato e viene usata come valore per proprietà come Margin. Inserendo un convertitore di tipi in Thickness, tutte le proprietà che usano una struttura Thickness risultano più facili da specificare in XAML, in quanto possono essere specificate come attributi. L'esempio seguente usa una conversione di tipi e la sintassi per attributi per specificare un valore per una proprietà Margin:

<Button Margin="10,20,10,30" Content="Click me"/>

L'esempio precedente di sintassi per attributi è equivalente all'esempio di sintassi più dettagliato riportato di seguito, in cui Margin è impostato invece tramite la sintassi per elementi proprietà contenente un elemento oggetto Thickness. Le quattro proprietà chiave di Thickness sono impostate come attributi nella nuova istanza:

<Button Content="Click me">
    <Button.Margin>
        <Thickness Left="10" Top="20" Right="10" Bottom="30"/>
    </Button.Margin>
</Button>

Nota

Esiste inoltre un numero limitato di oggetti in cui la conversione dei tipi rappresenta l'unico modo pubblico per impostare una proprietà su quel tipo senza coinvolgere una sottoclasse, perché il tipo stesso non include un costruttore senza parametri. Un esempio è Cursor.

Per altre informazioni sulla conversione dei tipi, vedere TypeConverter e XAML.

Elementi radice e spazi dei nomi

Per essere sia un file XML ben formato che un file XAML valido, un file XAML deve includere un solo elemento radice. Per scenari WPF tipici, è possibile usare un elemento radice dotato di un significato prominente nel modello di app WPF, ad esempio Window o Page per una pagina, ResourceDictionary per un dizionario esterno o Application per la definizione dell'app. L'esempio seguente illustra l'elemento radice di un tipico file XAML per una pagina WPF, con l'elemento radice di Page.

<Page x:Class="index.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Page1">

</Page>

L'elemento radice inoltre contiene gli attributi xmlns e xmlns:x, che indicano a un processore XAML gli spazi dei nomi XAML contenenti le definizioni dei tipi per i tipi di supporto a cui il markup farà riferimento come elementi. L'attributo xmlns indica in modo specifico lo spazio dei nomi XAML predefinito. All'interno dello spazio dei nomi XAML predefinito gli elementi oggetto nel markup possono essere specificati senza prefisso. Per la maggior parte degli scenari con app WPF e per quasi tutti gli esempi offerti nelle sezioni di WPF nell'SDK, viene eseguito il mapping dello spazio dei nomi XAML predefinito allo spazio dei nomi WPF http://schemas.microsoft.com/winfx/2006/xaml/presentation. L'attributo xmlns:x indica uno spazio dei nomi XAML aggiuntivo, che viene mappato allo spazio dei nomi del linguaggio XAML http://schemas.microsoft.com/winfx/2006/xaml.

Tale uso di xmlns per definire un ambito per l'uso e il mapping di un NameScope è coerente con la specifica XML 1.0. I NameScope XAML sono diversi dai NameScope XML solo in quanto nei primi vengono indicati anche alcuni aspetti correlati al supporto degli elementi del NameScope da parte dei tipi riguardo a risoluzione dei tipi e analisi di XAML.

Gli attributi xmlns sono strettamente necessari solo per l'elemento radice di ogni file XAML. Le definizioni xmlns si applicheranno a tutti gli elementi discendenti dell'elemento radice, un comportamento coerente con la specifica XML 1.0 per xmlns. Gli attributi xmlns sono inoltre consentiti in altri elementi sottostanti la radice e si applicherebbero a qualsiasi elemento discendente dell'elemento di definizione. Tuttavia, la definizione o ridefinizione frequente degli spazi dei nomi XAML può risultare in uno stile di markup XAML di difficile lettura.

L'implementazione WPF del relativo processore XAML include un'infrastruttura che rileva gli assembly principali WPF. Gli assembly principali WPF contengono notoriamente i tipi che supportano i mapping WPF allo spazio dei nomi XAML predefinito. Ciò è consentito tramite la configurazione inclusa nel file di compilazione del progetto e nei sistemi di compilazione e del progetto WPF. La dichiarazione dello spazio dei nomi XAML predefinito come xmlns predefinito è pertanto l'unica operazione necessaria per fare riferimento a elementi XAML provenienti da assembly WPF.

Prefisso x:

L'esempio di elemento radice precedente usa il prefisso x: per eseguire il mapping dello spazio dei nomi XAML http://schemas.microsoft.com/winfx/2006/xaml, che è lo spazio dei nomi XAML dedicato che supporta costrutti di linguaggio XAML. Il prefisso x: viene usato per il mapping di questo spazio dei nomi XAML nei modelli per i progetti, negli esempi e nella documentazione in tutto l'SDK. Lo spazio dei nomi XAML per il linguaggio XAML contiene diversi costrutti di programmazione che verranno usati frequentemente in XAML. Di seguito viene presentato un elenco dei più comuni costrutti di programmazione del prefisso x: che verranno usati:

  • x:Key: imposta una chiave univoca per ogni risorsa in un oggetto ResourceDictionary o concetti di dizionario simili di altri framework. x:Key sarà coinvolto probabilmente nel 90% degli utilizzi di x: osservati nel markup di un'app WPF tipica.

  • x:Class: specifica lo spazio dei nomi CLR e il nome della classe che offre il code-behind per una pagina XAML. È necessario che tale classe supporti il code-behind per ciascun modello di programmazione WPF ed è per questa ragione che quasi sempre viene eseguito il mapping di x:, anche se non sono presenti risorse.

  • x:Name: specifica il nome di un oggetto di runtime per l'istanza presente nel codice della fase di esecuzione dopo l'elaborazione di un elemento oggetto. In generale, si usa spesso una proprietà equivalente definita in WPF per x:Name. Tali proprietà eseguono specificamente il mapping a una proprietà di supporto CLR e risultano pertanto più utili per la programmazione di app, in cui spesso viene uso codice della fase di esecuzione per individuare gli elementi denominati dal linguaggio XAML inizializzato. La proprietà più comune di questo tipo è FrameworkElement.Name. È comunque possibile usare x:Name nei casi in cui la proprietà Name equivalente a livello di framework WPF non sia supportata in un tipo particolare. Tale situazione si verifica in determinati scenari di animazione.

  • x:Static: abilita il riferimento che restituisce un valore statico che non costituisce in altro modo una proprietà compatibile con XAML.

  • x:Type: crea un riferimento Type basato sul nome di un tipo. Viene usato per specificare attributi che accettano Type, ad esempio Style.TargetType, anche se spesso la proprietà prevede la conversione nativa da stringa a Type in modo da rendere facoltativo l'uso dell'estensione di markup x:Type.

Nel prefisso x: o nello spazio dei nomi XAML sono presenti altri costrutti di programmazione non altrettanto comuni. Per informazioni dettagliate, vedere Funzionalità del linguaggio dello spazio dei nomi XAML (x:).

Prefissi personalizzati e tipi personalizzati

Per gli assembly personalizzati o per gli assembly esterni agli elementi principali WPF di PresentationCore, PresentationFramework e WindowsBase, è possibile specificare l'assembly come parte di un mapping xmlns personalizzato. È quindi possibile fare riferimento ai tipi dell'assembly in XAML, purché tale tipo sia stato implementato correttamente per supportare gli usi di XAML desiderati.

Di seguito viene riportato un semplice esempio del funzionamento dei prefissi personalizzati nel markup XAML. Il prefisso custom è definito nel tag dell'elemento radice e viene mappato a un assembly specifico compresso e disponibile con l'app. Questo assembly contiene un tipo NumericUpDown, implementato per supportare l'uso generale di XAML, nonché l'uso di un'ereditarietà delle classi che ne consenta l'inserimento in questo punto specifico in un modello di contenuto XAML WPF. Un'istanza di questo controllo NumericUpDown viene dichiarata come elemento oggetto, usando il prefisso in modo tale che un parser XAML sia in grado di riconoscere lo spazio dei nomi XAML contenente il tipo e pertanto la posizione dell'assembly di supporto che contiene la definizione del tipo.

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"
    >
  <StackPanel Name="LayoutRoot">
    <custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>
...
  </StackPanel>
</Page>

Per altre informazioni sui tipi personalizzati in XAML, vedere XAML e classi personalizzate per WPF.

Per altre informazioni sulla correlazione tra gli spazi dei nomi XML e gli spazi dei nomi del codice negli assembly, vedere Spazi dei nomi XAML e mapping degli spazi dei nomi per XAML WPF.

Eventi e code-behind XAML

La maggior parte delle app WPF include sia markup che code-behind XAML. All'interno di un progetto, XAML viene scritto come file .xaml e per scrivere un file code-behind viene usato un linguaggio CLR come Microsoft Visual Basic o C#. Durante la compilazione dal markup di un file XAML come parte dei modelli di applicazione e di programmazione WPF, il percorso del file code-behind XAML per un file XAML viene identificato specificando uno spazio dei nomi e una classe come attributo x:Class dell'elemento radice della pagina XAML.

Gli esempi riportati fino a questo momento illustrano molti pulsanti, nessuno dei quali era associato a un comportamento logico. Il meccanismo primario a livello di applicazione per l'aggiunta di un comportamento per un elemento oggetto consiste nell'uso di un evento esistente della classe dell'elemento e nella scrittura di un gestore specifico per quell'evento che viene richiamato nel momento in cui l'evento viene generato in fase di esecuzione. Il nome dell'evento e il nome del gestore da usare sono specificati nel markup, mentre il codice che implementa il gestore è definito nel code-behind.

<Page x:Class="ExampleNamespace.ExamplePage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Button Click="Button_Click">Click me</Button>
    </StackPanel>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ExampleNamespace;

public partial class ExamplePage : Page
{
    public ExamplePage() =>
        InitializeComponent();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var buttonControl = (Button)e.Source;
        buttonControl.Foreground = Brushes.Red;
    }
}
Class ExamplePage
    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        Dim buttonControl = DirectCast(e.Source, Button)
        buttonControl.Foreground = Brushes.Red
    End Sub
End Class

Si noti che il file code-behind usa lo spazio dei nomi ExampleNamespace CLR (lo spazio dei nomi non è visibile in Visual Basic) e dichiara ExamplePage come classe parziale all'interno di tale spazio dei nomi. In questo modo viene eseguito il parallelismo del valore dell'attributo x:Class di ExampleNamespace.ExamplePage offerto nella radice del markup. Il compilatore del markup WPF creerà una classe parziale per ogni file XAML compilato derivando una classe dal tipo di elemento radice. Quando si offre code-behind che definisce anche la stessa classe parziale, il codice risultante viene combinato all'interno dello stesso spazio dei nomi e della stessa classe dell'app compilata.

Importante

In Visual Basic lo spazio dei nomi radice è implicito sia per XAML che per code-behind. Sono visibili solo gli spazi dei nomi annidati. Questo articolo illustra il codice XAML del progetto C#.

Per altre informazioni sui requisiti per la programmazione di code-behind in WPF, vedere Code-behind, gestore eventi e requisiti della classe parziale in WPF.

Se non si vuole creare un file code-behind distinto, è anche possibile incorporare il codice in un file XAML. Tuttavia, il codice inline è una tecnica meno versatile che presenta limitazioni sostanziali. Per altre informazioni, vedere Code-behind e XAML in WPF.

Eventi indirizzati

Un evento indirizzato è una particolare funzionalità evento fondamentale per WPF. Gli eventi indirizzati consentono a un elemento di gestire un evento generato da un elemento diverso, purché gli elementi siano connessi tramite una relazione di struttura ad albero. Quando si specifica la gestione dell'evento con un attributo XAML, l'evento indirizzato può essere ascoltato e gestito in qualsiasi elemento, inclusi quelli che non elencano l'evento specifico nella tabella dei membri della classe. Ciò è possibile qualificando l'attributo del nome evento con il nome della classe di appartenenza. Ad esempio, l'elemento padre StackPanel nell'esempio StackPanel / Button potrebbe registrare un gestore per l'evento Click del pulsante dell'elemento figlio specificando l'attributo Button.Click nell'elemento oggetto StackPanel e indicando il nome del gestore come valore dell'attributo. Per altre informazioni, vedere Cenni preliminari sugli eventi indirizzati.

Elementi denominati

Per impostazione predefinita, l'istanza di oggetto creata in un oggetto grafico tramite l'elaborazione di un elemento oggetto XAML non include un identificatore univoco o un riferimento a un oggetto. Al contrario, se si chiama un costruttore nel codice, quasi sempre il risultato del costruttore viene usato per impostare una variabile sull'istanza creata, in modo che sia possibile fare riferimento all'istanza nel codice in un secondo momento. Per offrire accesso standard agli oggetti creati tramite una definizione del markup, in XAML viene definito l'attributo x:Name. È possibile impostare il valore dell'attributo x:Name in qualsiasi elemento oggetto. All'interno del code-behind l'identificatore scelto è equivalente a una variabile dell'istanza che fa riferimento all'istanza costruita. Gli elementi denominati funzionano sotto ogni aspetto come istanze dell'oggetto (il nome fa riferimento a tale istanza) e il code-behind può fare riferimento agli elementi denominati per gestire le interazioni di runtime all'interno dell'app. Questa connessione tra istanze e variabili viene effettuata tramite il compilatore di markup XAML WPF e più specificatamente implica funzionalità e modelli non illustrati in questo articolo, ad esempio InitializeComponent.

Gli elementi XAML a livello di framework WPF ereditano una proprietà Name che equivale all'attributo x:Name definito in XAML. Altre classi offrono equivalenti a livello di proprietà per x:Name, che in genere viene definito anche come proprietà Name. In generale se non è possibile individuare una proprietà Name nella tabella dei membri dell'elemento o del tipo scelto, usare x:Name in alternativa. I valori x:Name offriranno a un elemento XAML un identificatore che può essere usato in fase di esecuzione dai sottosistemi specifici o dai metodi di utilità, ad esempio FindName.

Nell'esempio seguente Name viene impostato su un elemento StackPanel. Un gestore di un controllo Button in tale elemento StackPanel fa quindi riferimento a StackPanel tramite il relativo riferimento all'istanza buttonContainer impostato da Name.

<StackPanel Name="buttonContainer">
    <Button Click="RemoveThis_Click">Click to remove this button</Button>
</StackPanel>
private void RemoveThis_Click(object sender, RoutedEventArgs e)
{
    var element = (FrameworkElement)e.Source;
    
    if (buttonContainer.Children.Contains(element))
        buttonContainer.Children.Remove(element);
}
Private Sub RemoveThis_Click(sender As Object, e As RoutedEventArgs)
    Dim element = DirectCast(e.Source, FrameworkElement)

    If buttonContainer.Children.Contains(element) Then
        buttonContainer.Children.Remove(element)
    End If
End Sub

Come accade per una variabile, il nome XAML di un'istanza è governato da un concetto di ambito, per cui è possibile attivare nomi che siano univoci all'interno di un determinato ambito prevedibile. Il markup primario che definisce una pagina denota un NameScope XAML univoco, il cui limite è costituito dall'elemento radice della pagina. Altre origini di markup, tuttavia, possono interagire con una pagina in fase di esecuzione, ad esempio gli stili o i modelli all'interno degli stili, e tali origini di markup spesso includono NameScope XAML propri non necessariamente connessi al NameScope XAML della pagina. Per altre informazioni su x:Name e su NameScope XAML, vedere Name, Direttiva x:Name o NameScope WPF XAML.

Proprietà ed eventi associati

XAML specifica una funzionalità del linguaggio che consente di specificare determinate proprietà o eventi in qualsiasi elemento, anche se la proprietà o l'evento non esiste nelle definizioni del tipo per l'elemento su cui viene impostato. La versione delle proprietà di questa funzionalità è denominata proprietà associata, la versione degli eventi è denominata evento associato. Concettualmente, è possibile pensare alle proprietà associate e agli eventi associati come a membri globali che possono essere impostati in qualsiasi elemento o istanza di oggetto XAML. Tuttavia, tale elemento, classe o infrastruttura più ampia dovrà supportare un archivio delle proprietà di supporto per i valori associati.

Le proprietà associate in XAML vengono in genere usate tramite la sintassi per attributi. Per specificare una proprietà associata in questa sintassi, è necessario usare il formato ownerType.propertyName.

In apparenza, assomiglia all'uso di un elemento proprietà, tuttavia in questo caso il tipo ownerType specificato è sempre un tipo diverso rispetto all'elemento oggetto nel quale è impostata la proprietà associata. ownerType è il tipo che offre i metodi della funzione di accesso richiesti da un processore XAML per ottenere o impostare il valore della proprietà associata.

Lo scenario più comune per le proprietà associate consiste nel consentire agli elementi figlio di segnalare un valore di proprietà al relativo elemento padre.

Nell'esempio seguente viene illustrata la proprietà associata DockPanel.Dock. La classe DockPanel definisce le funzioni di accesso per DockPanel.Dock ed è proprietaria della proprietà associata. La classe DockPanel include inoltre la logica che scorre i relativi elementi figlio e verifica in modo specifico in ogni elemento la presenza di un valore impostato di DockPanel.Dock. Se individuato, quel valore viene usato durante il layout per posizionare gli elementi figlio. L'uso della proprietà associata DockPanel.Dock e questa funzionalità di posizionamento rappresentano infatti lo scenario opportuno per la classe DockPanel.

<DockPanel>
    <Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
    <Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>

In WPF la maggior parte o tutte le proprietà associate sono implementate anche come proprietà di dipendenza. Per altre informazioni, vedere Cenni preliminari sulle proprietà associate.

Gli eventi associati usano un formato simile di sintassi per attributi ownerType.eventName. Come per gli eventi non associati, il valore dell'attributo per un evento associato in XAML specifica il nome del metodo di gestione richiamato quando l'evento viene gestito nell'elemento. Gli usi di eventi associati in XAML WPF sono meno comuni. Per altre informazioni, vedere Cenni preliminari sugli eventi associati.

Tipi di base

Il markup XAML WPF sottostante e il relativo spazio dei nomi XAML sono una raccolta di tipi corrispondenti a oggetti CLR ed elementi di markup da usare in XAML. Tuttavia non è possibile eseguire il mapping di tutte le classi agli elementi. Classi astratte come ButtonBase e alcune classi di base non astratte vengono usate per l'ereditarietà nel modello a oggetti CLR. Le classi di base, incluse quelle astratte, continuano a essere importanti per lo sviluppo di XAML, in quanto ciascuno degli elementi XAML concreti eredita i membri da una classe di base nella relativa gerarchia. Spesso tali membri includono proprietà che possono essere impostate come attributi nell'elemento oppure eventi che possono essere gestiti. FrameworkElement è la classe dell'interfaccia utente di base concreta di WPF a livello di framework WPF. Durante la progettazione dell'interfaccia utente, si usano diverse classi Shape, Panel, Decorator oppure Control, che derivano tutte da FrameworkElement. Una classe di base correlata, FrameworkContentElement, supporta elementi orientati al documento utili per una presentazione del layout di flusso, tramite le API che eseguono il mirroring delle API in FrameworkElement. La combinazione di attributi a livello di elemento e di un modello a oggetti CLR offre un set di proprietà comuni che è possibile impostare nella maggior parte degli elementi XAML concreti, indipendentemente dall'elemento XAML specifico e dal relativo tipo sottostante.

Sicurezza

XAML è un linguaggio di markup che rappresenta direttamente la creazione di istanze di oggetti e la relativa esecuzione. Gli elementi creati in XAML, pertanto, hanno la stessa capacità di interagire con risorse di sistema (quali accesso di rete e I/O file system) del codice dell'app. Il codice XAML dispone anche dello stesso accesso alle risorse di sistema dell'app per l'hosting.

Sicurezza dall'accesso di codice in WPF

A differenza di .NET Framework, WPF per .NET non supporta la sicurezza dall'accesso di codice. Per altre informazioni, vedere le differenze della sicurezza dall'accesso di codice.

Caricare XAML dal codice

Sebbene sia possibile usare XAML per definire un'interfaccia utente completa, è talvolta opportuno definire solo una parte dell'interfaccia utente tramite XAML. Questa funzionalità può essere usata per:

  • Abilitare la personalizzazione parziale.
  • Consentire l'archiviazione locale delle informazioni sull'interfaccia utente.
  • Modellare un oggetto business.

La chiave per questi scenari è la classe XamlReader e il relativo metodo Load. L'input è un file XAML, mentre l'output è un oggetto che rappresenta l'intera struttura ad albero di oggetti di runtime creata dal markup. È possibile quindi inserire l'oggetto in modo che sia una proprietà di un altro oggetto già esistente nell'app. Purché la proprietà si trovi nel modello di contenuto e disponga di funzionalità di visualizzazione che notificano al motore di esecuzione che è stato aggiunto nuovo contenuto nell'app, è possibile modificare facilmente il contenuto di un'app in tramite il caricamento dinamico in XAML.

Vedi anche