Applicazione di stili e modelli
Per l'applicazione di stili e modelli di Windows Presentation Foundation (WPF) viene fatto riferimento a una serie di funzionalità (stili, modelli, trigger e storyboard) che consentono agli sviluppatori e ai progettisti di un'applicazione di realizzare effetti visivamente accattivanti e di creare un aspetto coerente per il prodotto. Nonostante sviluppatori e progettisti abbiano la possibilità di personalizzare ampiamente l'aspetto in base alla singola applicazione, l'utilizzo di un modello stilistico efficace si rende necessario per gestire e condividere l'aspetto tra più applicazioni e all'interno delle stesse. Questo modello viene fornito da Windows Presentation Foundation (WPF).
Un'altra funzionalità del modello stilistico di WPF consiste nella separazione della presentazione dalla logica. In questo modo i progettisti sono in grado di lavorare sull'aspetto di un'applicazione utilizzando esclusivamente XAML nello stesso momento in cui gli sviluppatori lavorano sulla logica di programmazione mediante C# o Visual Basic.
La sezione è incentrata sull'utilizzo di stili e modelli per l'applicazione, mentre non è incluso alcun concetto relativo all'associazione dati. Per informazioni sull'associazione dati, vedere Cenni preliminari sull'associazione dati.
È inoltre importante conoscere le risorse, ovvero quegli elementi che consentono di riutilizzare stili e modelli. Per ulteriori informazioni sulle risorse, vedere Cenni preliminari sulle risorse.
Nel presente argomento sono contenute le seguenti sezioni.
- Esempio di applicazione di stili e modelli
- Nozioni fondamentali sullo stile
- Modelli di dati
- Modelli di controllo
- Trigger
- Risorse e temi condivisi
- Argomenti correlati
Esempio di applicazione di stili e modelli
Gli esempi di codice utilizzati in questa panoramica si basano su un semplice esempio di foto illustrato nell'immagine seguente:
In questo semplice esempio di foto vengono utilizzate le funzionalità per l'applicazione di stili e modelli per creare un'esperienza utente visivamente coinvolgente. Nell'esempio sono inclusi due TextBlock elementi e un controllo ListBox associato a un elenco di immagini. Per l'esempio completo, vedere Esempio di introduzione agli stili e ai modelli (la pagina potrebbe essere in inglese).
Nozioni fondamentali sullo stile
Un oggetto Style costituisce una valida soluzione per applicare un insieme di valori di proprietà a più elementi. Considerare ad esempio gli elementi TextBlock seguenti e il relativo aspetto predefinito:
<TextBlock>My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>
È possibile modificare l'aspetto predefinito impostando direttamente proprietà quali FontSize e FontFamily in ogni elemento TextBlock. Tuttavia, per fare in modo che gli elementi TextBlock condividano alcune proprietà, è possibile creare un oggetto Style nella sezione Resources del file XAML, come illustrato di seguito:
<Window.Resources>
...
<!--A Style that affects all TextBlocks-->
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
</Style>
...
</Window.Resources>
Quando si imposta TargetType per lo stile sul tipo TextBlock, lo stile viene applicato a tutti gli elementi TextBlock nella finestra.
Ora gli elementi TextBlock appaiono come segue:
Estensione degli stili
Si supponga di volere che i due elementi TextBlock condividano alcuni valori di proprietà, ad esempio FontFamily e HorizontalAlignment centrato, ma anche che il testo "My Pictures" abbia alcune proprietà aggiuntive. A tale scopo è possibile creare un nuovo stile basato sul primo, come illustrato di seguito:
<Window.Resources>
...
<!--A Style that extends the previous TextBlock Style-->
<!--This is a "named style" with an x:Key of TitleText-->
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"
x:Key="TitleText">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Foreground">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="#90DDDD" />
<GradientStop Offset="1.0" Color="#5BFFFF" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
...
</Window.Resources>
Si noti che allo stile precedente viene assegnato un oggetto x:Key. Per applicare lo stile, impostare la proprietà Style di TextBlock sul valore x:Key, come illustrato di seguito:
<TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>
Ora lo stile di TextBlock ha un valore HorizontalAlignment equivalente a Center, un valore FontFamily equivalente a Comic Sans MS, un valore FontSize pari a 26 e un valore Foreground impostato sull'oggetto LinearGradientBrush illustrato nell'esempio. Si noti che viene eseguito l'override del valore FontSize dello stile di base. In presenza di più Setter che impostano la stessa proprietà in un oggetto Style, l'ultimo Setter dichiarato ha la precedenza.
Di seguito viene illustrato come appaiono ora gli elementi TextBlock:
Questo stile TitleText estende lo stile creato per il tipo TextBlock. È anche possibile estendere uno stile dotato di x:Key utilizzando il valore x:Key. Vedere in proposito l'esempio fornito per la proprietà BasedOn.
Relazione tra proprietà TargetType e attributo x:Key
Come illustrato nel primo esempio, se si imposta la proprietà TargetType in TextBlock senza assegnare un attributo x:Key allo stile, questo verrà applicato a tutti gli elementi TextBlock. In questo caso, x:Key viene implicitamente impostato su {x:Type TextBlock}. Ciò significa che se si imposta esplicitamente x:Key su un valore diverso da {x:Type TextBlock}, Style non viene applicato automaticamente a tutti gli elementi TextBlock. Al contrario, sarà necessario applicare lo stile agli elementi TextBlock in modo esplicito, utilizzando il valore x:Key. Se lo stile si trova nella sezione delle risorse e in esso non viene impostata la proprietà TargetType, allora è necessario fornire un attributo x:Key.
Oltre a fornire un valore predefinito per x:Key, la proprietà TargetType specifica il tipo al quale vengono applicate le proprietà del metodo di impostazione. Se non viene specificata la proprietà TargetType, sarà necessario qualificare le proprietà negli oggetti Setter con un nome di classe utilizzando la sintassi Property="ClassName.Property". Ad esempio, anziché impostare Property="FontSize", occorre impostare Property su "TextBlock.FontSize" o "Control.FontSize".
Si noti anche come molti controlli WPF siano costituiti da una combinazione di altri controlli WPF. Se si crea un stile da applicare a tutti i controlli di un tipo, è possibile che si ottengano risultati imprevisti. Se ad esempio si crea uno stile destinato al tipo TextBlock in un oggetto Window, lo stile verrà applicato a tutti i controlli TextBlock inclusi nella finestra, anche se TextBlock fa parte di un altro controllo, ad esempio ListBox.
Stili e risorse
È possibile utilizzare uno stile su qualsiasi elemento derivante da FrameworkElement o FrameworkContentElement. Il modo più comune per dichiarare un stile consiste nella dichiarare uno stile come risorsa nella sezione Resources di un file XAML, come illustrato negli esempi precedenti. In quanto risorse, gli stili rispettano le stesse regole di ambito che si applicano a tutte le risorse, ovvero il percorso in cui uno stile viene dichiarato influisce sul percorso in cui lo stesso può essere applicato. Se ad esempio lo stile viene dichiarato nell'elemento radice del file XAML di definizione di applicazione, tale stile potrà essere utilizzato in qualsiasi punto nell'applicazione. Se si crea un'applicazione per l'esplorazione e si dichiara lo stile in uno dei file XAML dell'applicazione, lo stile potrà essere utilizzato soltanto in quel file. Per ulteriori informazioni sulle regole di ambito per le risorse, vedere Cenni preliminari sulle risorse.
In aggiunta, è possibile trovare ulteriori informazioni su stili e risorse in Risorse e temi condivisi più avanti in questo argomento.
Impostazione di stili a livello di codice
Per assegnare un stile denominato a un elemento a livello di codice, ottenere lo stile dall'insieme di risorse e assegnarlo alla proprietà Style dell'elemento. Si noti che gli elementi in un insieme di risorse sono di tipo Object. È pertanto necessario eseguire il cast dello stile recuperato a un oggetto Style prima di assegnarlo alla proprietà Style. Ad esempio, per impostare lo stile TitleText definito su un oggetto TextBlock denominato textblock1, utilizzare il codice che segue:
textblock1.Style = CType(Me.Resources("TitleText"), Style)
textblock1.Style = (Style)(this.Resources["TitleText"]);
Una volta applicato uno stile, questo viene bloccato e non può essere in alcun modo modificato. Per modificare uno stile già applicato in modo dinamico, è necessario creare un nuovo stile che sostituisca quello esistente. Per ulteriori informazioni, vedere la proprietà IsSealed.
È possibile creare un oggetto che sceglie uno stile da applicare in base alla logica personalizzata. Vedere in proposito l'esempio fornito per la classe StyleSelector.
Associazioni, risorse dinamiche e gestori eventi
È possibile utilizzare la proprietà Setter.Value per specificare un'Associazione dell'estensione di markup o un'Estensione del markup DynamicResource. Per ulteriori informazioni, vedere gli esempi forniti per la proprietà Setter.Value.
In questa panoramica è stato finora illustrato l'utilizzo di metodi per l'impostazione di valori di proprietà. In uno stile è possibile specificare anche i gestori eventi. Per ulteriori informazioni, vedere EventSetter.
Modelli di dati
In questa applicazione di esempio è incluso un controllo ListBox associato a un elenco di foto:
<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>
ListBox appare attualmente come riportato di seguito:
La maggior parte dei controlli dispone di un tipo di contenuto che spesso proviene dai dati al quale vengono associati. In questo esempio, i dati sono rappresentati dall'elenco di foto. In WPF si utilizza un oggetto DataTemplate per definire la rappresentazione visiva dei dati. Gli elementi inseriti in un oggetto DataTemplate determinano essenzialmente l'aspetto dei dati nell'applicazione di cui viene eseguito il rendering.
Nell'applicazione di esempio, ogni oggetto Photo personalizzato possiede una proprietà Source di tipo string che specifica il percorso del file dell'immagine. Attualmente, gli oggetti foto appaiono come percorsi di file.
Per far apparire le foto come immagini, è necessario creare un oggetto DataTemplate sotto forma di risorsa:
<Window.Resources>
...
<!--DataTemplate to display Photos as images
instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
<Border Margin="3">
<Image Source="{Binding Source}"/>
</Border>
</DataTemplate>
...
</Window.Resources>
La proprietà DataType è molto simile alla proprietà TargetType di Style. Se DataTemplate si trova nella sezione delle risorse, quando si specifica un tipo per la proprietà DataType senza assegnare un attributo x:Key, verrà applicato DataTemplate ogni qualvolta appare il suddetto tipo. Resta sempre la possibilità di assegnare un attributo x:Key a DataTemplate, quindi impostarlo come StaticResource per le proprietà che accettano i tipi DataTemplate, ad esempio ItemTemplate o ContentTemplate.
Nell'esempio precedente DataTemplate stabilisce essenzialmente che, ogni qualvolta è presente un oggetto Photo, questo deve apparire come Image all'interno di un oggetto Border. Con questo DataTemplate, l'applicazione appare come riportato di seguito:
L'applicazione di un modello di dati consente di utilizzare altre funzionalità. Ad esempio, in caso di visualizzazione di dati dell'insieme contenenti altri insiemi mediante un tipo HeaderedItemsControl quale ad esempio Menu o TreeView, è possibile utilizzare HierarchicalDataTemplate. Un'altra funzionalità è DataTemplateSelector, che consente di scegliere un oggetto DataTemplate da utilizzare in base alla logica personalizzata. Per ulteriori informazioni, vedere Cenni preliminari sui modelli di dati, in cui vengono trattate più dettagliatamente le diverse funzionalità dei modelli di dati.
Modelli di controllo
In WPF l'oggetto ControlTemplate di un controllo ha lo scopo di definirne l'aspetto. È possibile modificare la struttura e l'aspetto di un controllo attraverso la definizione di un nuovo oggetto ControlTemplate per il controllo. In molti casi, questa procedura offre una flessibilità sufficiente da non richiedere la scrittura di controlli personalizzati. Per ulteriori informazioni, vedere Personalizzazione dell'aspetto di un controllo esistente mediante la creazione di un oggetto ControlTemplate.
Trigger
Un trigger determina l'impostazione di proprietà o l'avvio di azioni, ad esempio un'animazione, quando un valore di proprietà viene modificato oppure viene generato un evento. Style, ControlTemplate e DataTemplate possiedono tutti una proprietà Triggers che può contenere un insieme di trigger. Esistono vari tipi di trigger.
Trigger di proprietà
Un oggetto Trigger che determina l'impostazione di valori di proprietà o l'avvio di azioni in base al valore di una proprietà è detto trigger di proprietà.
Per illustrare l'utilizzo dei trigger di proprietà, è possibile rendere ogni oggetto ListBoxItem parzialmente trasparente quando non è selezionato. Il seguente stile imposta il valore Opacity di un oggetto ListBoxItem su 0.5. Quando la proprietà IsSelected è true, invece, Opacity è impostato su 1.0:
<Style TargetType="ListBoxItem">
<Setter Property="Opacity" Value="0.5" />
<Setter Property="MaxHeight" Value="75" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Opacity" Value="1.0" />
</Trigger>
...
</Style.Triggers>
</Style>
In questo esempio viene utilizzato un oggetto Trigger per impostare un valore di proprietà, ma è bene ricordare che la classe Trigger possiede anche le proprietà EnterActions e ExitActions che consentono a un trigger di eseguire determinate azioni.
Si noti che la proprietà MaxHeight dell'oggetto ListBoxItem è impostata su 75. Nell'illustrazione seguente il terzo elemento è l'elemento selezionato:
EventTrigger e Storyboard
Un altro tipo di trigger è EventTrigger, il quale avvia una serie di azioni in base all'occorrenza di un evento. Ad esempio, gli oggetti EventTrigger seguenti specificano che quando il puntatore del mouse entra in ListBoxItem, la proprietà MaxHeight assume un valore pari a 90 per un intervallo di 0.2 secondi. Quando il mouse si sposta dall'elemento, la proprietà ritorna al valore originale in 1 secondo. Non è necessario specificare un valore To per l'animazione MouseLeave. Questo perché l'animazione è in grado di tenere traccia del valore originale.
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetProperty="MaxHeight"
To="90" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:1"
Storyboard.TargetProperty="MaxHeight" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
Per ulteriori informazioni, vedere Cenni preliminari sugli storyboard.
Nell'illustrazione seguente il mouse è posizionato sul terzo elemento:
MultiTrigger, DataTrigger e MultiDataTrigger
Oltre a Trigger e EventTrigger, esistono altri tipi di trigger. MultiTrigger consente di impostare valori di proprietà in base a più condizioni. DataTrigger e MultiDataTrigger vengono invece utilizzati quando la proprietà della condizione è associata a dati.
Risorse e temi condivisi
Un'applicazione Windows Presentation Foundation (WPF) tipica può disporre di più risorse dell'interfaccia utente applicate nell'intero ambito dell'applicazione. Nel suo complesso questo set di risorse può essere considerato come il tema per l'applicazione. Windows Presentation Foundation (WPF) fornisce un supporto per l'assemblaggio delle risorse dell'interfaccia utente sotto forma di tema mediante un dizionario risorse incapsulato come classe ResourceDictionary.
I temi Windows Presentation Foundation (WPF) vengono definiti utilizzando il meccanismo di applicazione di stili e modelli esposto da Windows Presentation Foundation (WPF) per la personalizzazione degli oggetti visivi di un elemento.
Le risorse dei temi Windows Presentation Foundation (WPF) vengono archiviate in dizionari risorse incorporati. Questi dizionari devono essere incorporati all'interno di un assembly firmato e possono essere incorporati nello stesso assembly del codice o in un assembly side-by-side. Nel caso di PresentationFramework.dll, l'assembly contenente i controlli Windows Presentation Foundation (WPF), le risorse del tema sono contenute in una serie di assembly side-by-side.
Il tema è in assoluto l'ultimo posto in cui cercare lo stile di un elemento. In genere, la ricerca inizia risalendo la struttura ad albero dell'elemento in cerca di una risorsa appropriata, prosegue quindi nell'insieme delle risorse dell'applicazione, per poi terminare con una query al sistema. In questo modo gli sviluppatori di applicazioni possono ridefinire lo stile per qualsiasi oggetto a livello di struttura ad albero o di applicazione prima di arrivare al tema.
È possibile definire i dizionari risorse come singoli file che consentono di riutilizzare un tema all'interno di più applicazioni. È anche possibile creare temi scambiabili definendo più dizionari risorse che forniscono gli stessi tipi di risorse ma con valori diversi. La ridefinizione di questi stili o di altre risorse a livello di applicazione è l'approccio consigliato per definire l'interfaccia di un'applicazione.
Per condividere un set di risorse, inclusi stili e modelli, tra diverse applicazioni, è possibile creare un file XAML e definire un oggetto ResourceDictionary. Per un esempio, osservare l'illustrazione seguente in cui viene illustrata una parte dell'Esempio di applicazione di stili con ControlTemplates (la pagina potrebbe essere in inglese).
Se si osservano i file XAML dell'esempio, si noterà che tutti i file dispongono del codice seguente:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>
Si tratta della condivisione di shared.xaml, che definisce un oggetto ResourceDictionary contenente un insieme di risorse di stile e riempimento in grado di conferire un aspetto coerente ai controlli nell'esempio.
Per ulteriori informazioni, vedere Dizionari risorse uniti.
Per la creazione di un tema per il controllo personalizzato, vedere la sezione Libreria di controlli esterna in Cenni preliminari sulla modifica di controlli.
Vedere anche
Attività
Procedura: trovare elementi generati da un oggetto ControlTemplate
Procedura: trovare elementi generati da un oggetto DataTemplate