Panoramica delle risorse XAML (WPF .NET)

Una risorsa è un oggetto che può essere riutilizzato in posizioni diverse nell'app. Sono esempi di risorse pennelli e stili. Questa panoramica descrive come usare le risorse in XAML (Extensible Application Markup Language). È anche possibile creare e accedere alle risorse usando il codice.

Nota

Le risorse XAML descritte in questo articolo sono diverse dalle risorse dell'app, che in genere sono file aggiunti a un'app, ad esempio contenuto, dati o file incorporati.

Importante

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

Usare le risorse in XAML

L'esempio seguente definisce un oggetto SolidColorBrush come risorsa nell'elemento radice di una pagina. L'esempio fa quindi riferimento alla risorsa e lo usa per impostare le proprietà di diversi elementi figlio, tra cui Ellipse, e TextBlock.Button

<Window x:Class="resources.ResExample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ResExample" Height="400" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="#05E0E9"/>
        <Style TargetType="Border">
            <Setter Property="Background" Value="#4E1A3D" />
            <Setter Property="BorderThickness" Value="5" />
            <Setter Property="BorderBrush">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Offset="0.0" Color="#4E1A3D"/>
                        <GradientStop Offset="1.0" Color="Salmon"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="TextBlock" x:Key="TitleText">
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="Foreground" Value="#4E87D4"/>
            <Setter Property="FontFamily" Value="Trebuchet MS"/>
            <Setter Property="Margin" Value="0,10,10,10"/>
        </Style>
        <Style TargetType="TextBlock" x:Key="Label">
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="Foreground" Value="{StaticResource MyBrush}"/>
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="0,3,10,0"/>
        </Style>
    </Window.Resources>

    <Border>
        <StackPanel>
            <TextBlock Style="{StaticResource TitleText}">Title</TextBlock>
            <TextBlock Style="{StaticResource Label}">Label</TextBlock>
            <TextBlock HorizontalAlignment="Right" FontSize="36" Foreground="{StaticResource MyBrush}" Text="Text" Margin="20" />
            <Button HorizontalAlignment="Left" Height="30" Background="{StaticResource MyBrush}" Margin="40">Button</Button>
            <Ellipse HorizontalAlignment="Center" Width="100" Height="100" Fill="{StaticResource MyBrush}" Margin="10" />
        </StackPanel>
    </Border>

</Window>

Ogni elemento a livello di framework (FrameworkElement o FrameworkContentElement) ha una Resources proprietà , ovvero un ResourceDictionary tipo che contiene risorse definite. È possibile definire le risorse in qualsiasi elemento, ad esempio .Button Tuttavia, le risorse vengono spesso definite nell'elemento radice, che si trova Window nell'esempio.

Ogni risorsa in un dizionario risorse deve avere una chiave univoca. Quando si definiscono risorse in un markup, si assegna la chiave univoca tramite la direttiva x:Key. In genere, la chiave è una stringa. È tuttavia possibile impostare la chiave su altri tipi di oggetto tramite le estensioni di markup appropriate. Le chiavi non stringa per le risorse vengono usate da determinate aree di funzionalità in WPF, in particolare per gli stili, le risorse dei componenti e lo stile dei dati.

È possibile usare una risorsa definita con la sintassi dell'estensione di markup delle risorse che specifica il nome della chiave della risorsa. Ad esempio, usare la risorsa come valore di una proprietà su un altro elemento.

<Button Background="{StaticResource MyBrush}"/>
<Ellipse Fill="{StaticResource MyBrush}"/>

Nell'esempio precedente, quando il caricatore XAML elabora il valore {StaticResource MyBrush} per la Background proprietà in Button, la logica di ricerca della risorsa controlla innanzitutto il dizionario risorse per l'elemento Button . Se Button non dispone di una definizione della chiave MyBrush di risorsa (in questo esempio non lo è; la relativa raccolta di risorse è vuota), la ricerca successiva controlla l'elemento padre di Button. Se la risorsa non è definita nell'elemento padre, continua a controllare l'albero logico dell'oggetto verso l'alto fino a quando non viene trovato.

Se si definiscono le risorse nell'elemento radice, tutti gli elementi dell'albero logico, ad esempio Window o Page, possono accedervi. È anche possibile riutilizzare la stessa risorsa per impostare il valore di qualsiasi proprietà che accetta lo stesso tipo rappresentato dalla risorsa. Nell'esempio precedente la stessa MyBrush risorsa imposta due proprietà diverse: Button.Background e Ellipse.Fill.

Risorse statiche e dinamiche

È possibile fare riferimento a una risorsa come statica o dinamica. I riferimenti vengono creati usando l'estensione di markup StaticResource o l'estensione di markup DynamicResource. Un'estensione di markup è una funzionalità XAML che consente di specificare un riferimento a un oggetto, avendo l'estensione di markup elaborare la stringa dell'attributo e restituire l'oggetto a un caricatore XAML. Per altre informazioni sulle estensioni di markup, vedere Estensioni di markup e XAML WPF.

Quando si usa un'estensione di markup, in genere si forniscono uno o più parametri in formato stringa elaborati da tale estensione di markup specifica. L'estensione di markup StaticResource elabora una chiave cercando il valore della chiave stessa in tutti i dizionari risorse disponibili. L'elaborazione avviene durante il caricamento, ovvero quando il processo di caricamento deve assegnare il valore della proprietà. L'estensione di markup DynamicResource elabora invece una chiave creando un'espressione e tale espressione rimane non valutata fino all'esecuzione dell'app, quando l'espressione viene valutata per fornire un valore.

Quando si fa riferimento a una risorsa, la decisione di usare un riferimento di risorsa statica o un riferimento di risorsa dinamica può essere influenzata dalle considerazioni seguenti:

  • Quando si determina la progettazione complessiva della modalità di creazione delle risorse per l'app (per pagina, nell'app, in XAML libero o in un assembly di sola risorsa), tenere presente quanto segue:

  • Funzionalità dell'app. L'aggiornamento delle risorse in tempo reale fa parte dei requisiti dell'app?

  • Il comportamento di ricerca specifico del tipo di riferimento di risorsa.

  • La proprietà o il tipo di risorsa specifico e il comportamento nativo di tale tipo.

Risorse statiche

I riferimenti di risorse statiche sono ideali nelle circostanze seguenti:

  • La progettazione dell'app concentra la maggior parte delle risorse in dizionari di risorse a livello di pagina o di applicazione.

    I riferimenti alle risorse statiche non vengono rivalutati in base ai comportamenti di runtime, ad esempio ricaricando una pagina. È quindi possibile ottenere alcuni vantaggi in merito alle prestazioni per evitare un numero elevato di riferimenti dinamici alle risorse quando non sono necessari in base alla progettazione di risorse e app.

  • Si sta impostando il valore di una proprietà che non si trova in un DependencyObject oggetto o .Freezable

  • Si sta creando un dizionario risorse compilato in una DLL condivisa tra le app.

  • Si sta creando un tema per un controllo personalizzato e si definiscono le risorse usate all'interno dei temi.

    In questo caso, in genere non si vuole che il comportamento di ricerca delle risorse dinamiche faccia riferimento alle risorse. Usare invece il comportamento di riferimento alle risorse statiche in modo che la ricerca sia prevedibile e autonoma per il tema. Con un riferimento di risorsa dinamica, anche un riferimento all'interno di un tema viene lasciato non valutato fino alla fase di esecuzione. Inoltre, c'è la possibilità che quando viene applicato il tema, alcuni elementi locali ridefiniranno una chiave che il tema sta tentando di fare riferimento e l'elemento locale cadrà prima del tema stesso nella ricerca. In questo caso, il tema non si comporta come previsto.

  • Si usano risorse per impostare un numero elevato di proprietà di dipendenza. Le proprietà di dipendenza hanno un valore effettivo nella memorizzazione nella cache come abilitato dal sistema di proprietà, quindi se si fornisce un valore per una proprietà di dipendenza che può essere valutata in fase di caricamento, la proprietà di dipendenza non deve verificare la presenza di un'espressione rivalutata e può restituire l'ultimo valore effettivo. Questa tecnica può garantire un miglioramento delle prestazioni.

  • Si vuole modificare la risorsa sottostante per tutti i consumer o si vogliono mantenere istanze scrivibili separate per ogni consumer tramite l'attributo x: Shared.

Comportamento di ricerca delle risorse statiche

Di seguito viene descritto il processo di ricerca che si verifica automaticamente quando una risorsa statica fa riferimento a una proprietà o a un elemento:

  1. Il processo di ricerca controlla la presenza della chiave richiesta all'interno del dizionario risorse definito dall'elemento che imposta la proprietà.

  2. Il processo di ricerca attraversa quindi l'albero logico verso l'alto fino all'elemento padre e al relativo dizionario risorse. Questo processo continua finché non viene raggiunto l'elemento radice.

  3. Vengono controllate le risorse dell'app. Le risorse dell'app sono quelle risorse all'interno del dizionario risorse definito dall'oggetto per l'app Application WPF.

I riferimenti di risorse statiche all'interno di un dizionario risorse devono fare riferimento a una risorsa già definita lessicalmente prima del riferimento. I riferimenti di risorse statiche non sono in grado di risolvere riferimenti in avanti. Per questo motivo, progettare la struttura del dizionario risorse in modo che le risorse siano definite all'inizio di ogni rispettivo dizionario risorse.

La ricerca di risorse statiche può estendersi in temi o in risorse di sistema, ma questa ricerca è supportata solo perché il caricatore XAML rinvia la richiesta. Il differire è necessario in modo che il tema di runtime al momento del caricamento della pagina venga applicato correttamente all'app. Tuttavia, i riferimenti statici alle risorse statiche alle chiavi note solo nei temi o come risorse di sistema non sono consigliati, perché tali riferimenti non vengono rivalutati se il tema viene modificato dall'utente in tempo reale. Un riferimento di risorsa dinamica è più affidabile se si richiedono risorse di tema o di sistema. Fa eccezione il caso in cui un elemento tema stesso richieda un'altra risorsa. Per i motivi descritti in precedenza, questi riferimenti devono essere riferimenti di risorse statiche.

Il comportamento dell'eccezione se non viene trovato un riferimento a una risorsa statica varia. Se la risorsa è stata rinviata, l'eccezione si verifica in runtime. Se la risorsa non è stata posticipata, l'eccezione si verifica in fase di caricamento.

Risorse dinamiche

Le risorse dinamiche funzionano meglio quando:

  • Il valore della risorsa, incluse le risorse di sistema o le risorse che altrimenti sono impostabili dall'utente, dipende dalle condizioni che non sono note fino al runtime. Ad esempio, è possibile creare valori setter che fanno riferimento alle proprietà di sistema esposte da SystemColors, SystemFontso SystemParameters. Questi valori sono realmente dinamici perché in ultima analisi derivano dall'ambiente di runtime dell'utente e dal sistema operativo. È anche possibile che eventuali temi a livello di applicazione siano soggetti a modifica e che l'accesso alle risorse a livello di pagina debba anche acquisire la modifica.

  • Si stanno creando o facendo riferimento agli stili del tema per un controllo personalizzato.

  • Si intende modificare il contenuto di un ResourceDictionary oggetto durante la durata di un'app.

  • La struttura delle risorse è complessa e con interdipendenze e potrebbe richiedere riferimenti in avanti. I riferimenti alle risorse statiche non supportano i riferimenti forward, ma i riferimenti dinamici alle risorse li supportano perché la risorsa non deve essere valutata fino al runtime e i riferimenti in avanti non sono quindi un concetto pertinente.

  • Si fa riferimento a una risorsa di grandi dimensioni dal punto di vista di una compilazione o di un working set e la risorsa potrebbe non essere usata immediatamente quando la pagina viene caricata. I riferimenti alle risorse statiche vengono sempre caricati da XAML quando la pagina viene caricata. Tuttavia, un riferimento di risorsa dinamica non viene caricato fino a quando non viene usato.

  • Si sta creando uno stile in cui i valori di setter possono provenire da altri valori influenzati dai temi o da altre impostazioni utente.

  • Si stanno applicando risorse a elementi che potrebbero essere replicati nell'albero logico durante la durata dell'app. La modifica dell'elemento padre cambia potenzialmente anche l'ambito di ricerca. Se quindi si vuole che la risorsa per un elemento associato a un nuovo elemento padre sia rivalutata in base al nuovo ambito, usare sempre un riferimento di risorsa dinamica.

Comportamento di ricerca delle risorse dinamiche

Il comportamento di ricerca delle risorse per un riferimento di risorsa dinamica parallela al comportamento di ricerca nel codice se si chiama FindResource o SetResourceReference:

  1. La ricerca controlla la chiave richiesta all'interno del dizionario risorse definito dall'elemento che imposta la proprietà :

  2. La ricerca attraversa l'albero logico verso l'alto fino all'elemento padre e al relativo dizionario risorse. Questo processo continua finché non viene raggiunto l'elemento radice.

  3. Vengono controllate le risorse dell'app. Le risorse dell'app sono quelle risorse all'interno del dizionario risorse definite dall'oggetto per l'app Application WPF.

  4. Il dizionario risorse del tema viene controllato per il tema attualmente attivo. Se il tema viene modificato in runtime, il valore viene rivalutato.

  5. Vengono controllate le risorse di sistema.

Il comportamento di eventuali eccezioni varia:

  • Se una risorsa è stata richiesta da una FindResource chiamata e non è stata trovata, viene generata un'eccezione.

  • Se una risorsa è stata richiesta da una TryFindResource chiamata e non è stata trovata, non viene generata alcuna eccezione e il valore restituito è null. Se la proprietà impostata non accetta null, è comunque possibile che venga generata un'eccezione più profonda, a seconda della singola proprietà impostata.

  • Se una risorsa è stata richiesta da un riferimento di risorsa dinamica in XAML e non è stata trovata, il comportamento dipende dal sistema di proprietà generale. Il comportamento generale è come se non si verificasse alcuna operazione di impostazione della proprietà a livello in cui esiste la risorsa. Ad esempio, se si tenta di impostare lo sfondo su un singolo elemento pulsante usando una risorsa che non è stato possibile valutare, non viene impostato alcun valore, ma il valore effettivo può comunque provenire da altri partecipanti nel sistema di proprietà e precedenza del valore. Ad esempio, il valore di sfondo potrebbe comunque provenire da uno stile di pulsante definito localmente o dallo stile del tema. Per le proprietà non definite dagli stili del tema, il valore effettivo dopo una valutazione delle risorse non riuscita potrebbe provenire dal valore predefinito nei metadati della proprietà.

Limitazioni

I riferimenti di risorse dinamiche presentano alcune restrizioni rilevanti. Almeno una delle condizioni seguenti deve essere vera:

Poiché la proprietà impostata deve essere una DependencyProperty proprietà o Freezable , la maggior parte delle modifiche alle proprietà può essere propagata all'interfaccia utente perché una modifica della proprietà (il valore della risorsa dinamica modificata) viene riconosciuta dal sistema di proprietà. La maggior parte dei controlli include la logica che forza un altro layout di un controllo se una DependencyProperty modifica e tale proprietà potrebbe influire sul layout. Tuttavia, non tutte le proprietà che dispongono di un'estensione di markup DynamicResource perché il relativo valore è garantito di fornire aggiornamenti in tempo reale nell'interfaccia utente. Tale funzionalità può comunque variare a seconda della proprietà e a seconda del tipo proprietario della proprietà o anche della struttura logica dell'app.

Stili, datatemplate e chiavi implicite

Anche se tutti gli elementi in un ResourceDictionary oggetto devono avere una chiave, ciò non significa che tutte le risorse devono avere un oggetto esplicito x:Key. Diversi tipi di oggetto supportano una chiave implicita quando sono definiti come risorsa in cui il valore della chiave è associato al valore di un'altra proprietà. Questo tipo di chiave è noto come chiave implicita e un x:Key attributo è una chiave esplicita. È possibile sovrascrivere qualsiasi chiave implicita specificando una chiave esplicita.

Uno scenario importante per le risorse è quando si definisce un oggetto Style. Infatti, un Style oggetto è quasi sempre definito come voce in un dizionario risorse, perché gli stili sono intrinsecamente destinati al riutilizzo. Per altre informazioni sugli stili, vedere Stili e modelli (WPF .NET).

Una chiave implicita consente sia di creare stili per controlli che di fare riferimento ad essi. Gli stili di tema che definiscono l'aspetto predefinito di un controllo si basano sulla chiave implicita. Dal punto di vista della richiesta, la chiave implicita è il Type del controllo stesso. Dal punto di vista della definizione delle risorse, la chiave implicita è l'oggetto TargetType dello stile. Di conseguenza, se si creano temi per controlli personalizzati o si creano stili che interagiscono con gli stili del tema esistenti, non è necessario specificare una direttiva x:Key per tale Styleoggetto . E se vuoi usare gli stili con tema, non devi specificare alcuno stile. Ad esempio, la definizione di stile seguente funziona, anche se la Style risorsa non sembra avere una chiave:

<Style TargetType="Button">
    <Setter Property="Background" Value="#4E1A3D" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="BorderThickness" Value="5" />
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush>
                <GradientStop Offset="0.0" Color="#4E1A3D"/>
                <GradientStop Offset="1.0" Color="Salmon"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
</Style>

Questo stile ha davvero una chiave: la chiave implicita: il System.Windows.Controls.Button tipo. Nel markup è possibile specificare un TargetType oggetto direttamente come nome del tipo (oppure facoltativamente usare {x:Type...} per restituire un oggetto Type.

Tramite i meccanismi di stile del tema predefiniti usati da WPF, tale stile viene applicato come stile di runtime di un oggetto Button nella pagina, anche se non Button tenta di specificare la relativa Style proprietà o un riferimento di risorsa specifico allo stile. Lo stile definito nella pagina viene trovato in precedenza nella sequenza di ricerca rispetto allo stile del dizionario dei temi, usando la stessa chiave dello stile del dizionario dei temi. È sufficiente specificare <Button>Hello</Button> in qualsiasi punto della pagina e lo stile definito con TargetType di Button si applica a tale pulsante. Se si vuole, è comunque possibile modificare in modo esplicito lo stile con lo stesso valore TargetType del tipo specificato per maggiore chiarezza nel markup, ma questo è facoltativo.

Le chiavi implicite per gli stili non si applicano a un controllo se OverridesDefaultStyle è true. Si noti anche che OverridesDefaultStyle potrebbe essere impostato come parte del comportamento nativo per la classe di controllo, anziché in modo esplicito in un'istanza del controllo. Inoltre, per supportare chiavi implicite per gli scenari di classi derivate, il controllo deve eseguire l'override DefaultStyleKey (tutti i controlli esistenti forniti come parte di WPF includono questa sostituzione). Per altre informazioni sulla progettazione di stili, temi e controlli, vedere Linee guida per la progettazione di controlli a cui è possibile applicare degli stili.

DataTemplate ha anche una chiave implicita. La chiave implicita per un DataTemplate oggetto è il valore della DataType proprietà. DataType può anche essere specificato come nome del tipo anziché usare in modo esplicito {x:Type...}. Per i dettagli, vedere Cenni preliminari sui modelli di dati.

Vedi anche