Gli stili in Windows Presentation Foundation
a cura di Alessandro Del Sole (requisiti: conoscenze di base di WPF)Cosa sono gli stili
Una caratteristica molto importante di Windows Presentation Foundation è costituita dagli stili grafici.
Come potete immaginare, uno stile definisce un gruppo di proprietà che è possibile assegnare a un determinato tipo di controlli per influenzarne l'aspetto estetico. I controlli, poi, possono conformarsi allo stile definito tramite una semplice dichiarazione scritta in codice XAML.
Nel terzo articolo della serie introduttiva su WPF, abbiamo visto un piccolo esempio di stile applicato ai pulsanti dell'applicazione di esempio. Pertanto, se avete letto l'articolo, vi siete già fatti un'idea dell'argomento che stiamo affrontando.In questo articolo, ci occuperemo di esaminare alcune ulteriori caratteristiche degli stili, cercando di capire come padroneggiarli e perfezionarli a seconda delle proprie esigenze. Poiché l'implementazione degli stili è una tecnica che riguarda prevalentemente la fase di design, in questa sede utilizzeremo esclusivamente codice XAML per disegnare dei pulsanti stilizzati.
Per capire come interagire da codice Visual Basic e C# con i pulsanti (e più in generale con i controlli), tramite i file di code-behind, potete leggere il secondo articolo della serie introduttiva su WPF.A corredo di questo articolo, pertanto, ho predisposto un archivio .zip, contenente il codice XAML che andremo ad esaminare e che, per comodità, ho suddiviso in singoli file .xaml, piuttosto che in progetti per VB e C#, che potrete aggiungere manualmente ai vostri progetti. L'archivio in questione è disponibile in area download di VB T&T.
Un esempio di stile
Aprite Visual Studio 2005 e, tramite il comando Nuovo|Progetto del menu File, create un nuovo progetto vuoto basato su Windows Presentation Foundation, utilizzando il linguaggio di programmazione che preferite.
Prima di scrivere codice, consideriamo alcuni concetti.
Innanzitutto, gli stili risiedono all'interno delle risorse di un contenitore di controlli (es. Window.Resources, StackPanel.Resources e così via).
In secondo luogo, ogni proprietà di uno stile è definita da elementi Setter.
Un elemento Setter, contraddistinto dai tag <Setter></Setter>, consente di specificare la proprietà da impostare (attributo Property) e il relativo valore (attributo Value).
Nell'editor di codice XAML, digitate il seguente codice all'interno dei tag <Window></Window> (il codice sarà commentato in seguito):<Window.Resources> <Style x:Key="MyStyledButton" TargetType="Button"> <Setter Property="FontFamily" Value="Courier New" /> <Setter Property="FontSize" Value="12" /> <Setter Property="FontWeight" Value="Bold" /> <Setter Property="Width" Value="150" /> <Setter Property="Height" Value="75" /> <Setter Property="Background" > <Setter.Value> <LinearGradientBrush> <GradientStop Color="Yellow" Offset="0"/> <GradientStop Color="Green" Offset="0.5"/> <GradientStop Color="Violet" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="Foreground"> <Setter.Value> <LinearGradientBrush> <GradientStop Color="Orange" Offset="0"/> <GradientStop Color="Black" Offset="0.5"/> <GradientStop Color="Red" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> </Style> </Window.Resources>Gli stili sono definiti nelle risorse di un contenitore. In questo caso abbiamo usato Window, ma sarebbe potuto essere, ad esempio, la Grid.
L'elemento Style indica l'inizio della definizione dello stile e le proprietà x:Key e TargetType indicano, rispettivamente, un identificatore per lo stile e il tipo di controllo cui lo stesso si riferisce (in questo esempio lo stile è definito per i pulsanti, ma può essere un altro tipo di controllo).
L'elemento Setter consente di impostare delle proprietà del controllo (attributo Property) sul relativo valore (attributo Value).
Nell'esempio, abbiamo innanzitutto definito le proprietà relative al font (proprietà FontFamily, FontSize e FontWeight), quindi le dimensioni del pulsante (Width ed Height).E' interessante capire come, grazie alla consueta struttura gerarchica di XAML, sia possibile anche definire lo sfondo del pulsante e il colore di primo piano del testo tramite gli oggetti Brush (in questo caso, abbiamo usato il LinearGradientBrush in entrambi i casi per definire dei gradienti). Poiché la proprietà Value non è costituita da un valore singolo, abbiamo dovuto suddividere il tag Setter (Setter.Value), avendo la possibilità di definire al suo interno altri elementi.
Fatto questo, si disegna il pulsante. Come potete osservare, per utilizzare lo stile è sufficiente assegnare un attributo Style all'elemento Button, facendo riferimento alla risorsa (StaticResource), cui abbiamo precedentemente assegnato l'identificatore.
Avviando l'applicazione, questo è il simpatico risultato che otteniamo a video:![]()
Lo stile definito può così essere assegnato ad altri pulsanti.
I Trigger
Nel paragrafo precedente abbiamo visto come, tramite uno stile, tutte le proprietà stabilite dichiarando elementi Setter vengano applicate incondizionatamente ai controlli cui lo stile è destinato. Sarebbe auspicabile poter applicare una determinata proprietà ad un controllo solo al verificarsi di una determinata condizione, ad esempio se il puntatore del mouse passa sopra al controllo.
In WPF, questo problema viene risolto brillantemente, mediante l'utilizzo degli oggetti Trigger, caratterizzati, in XAML, dalla dichiarazione di un elemento <Trigger></Trigger>. Grazie ai Trigger, è possibile applicare i valori delle proprietà stabilite negli elementi Setter solo al verificarsi di una determinata condizione.
Riprendendo il codice XAML evidenziato nel paragrafo precedente, aggiungete il seguente codice prima del tag di chiusura </Style>:<Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0" Color="Black" /> <GradientStop Offset="0.5" Color="Gainsboro" /> <GradientStop Offset="1" Color="White" /> </LinearGradientBrush> </Setter.Value> </Setter> </Trigger> </Style.Triggers>L'elemento Setter imposterà il valore della proprietà Background del controllo (nell'esempio un pulsante) solo qualora la proprietà IsMouseOver sia impostata su True (cioè quando il puntatore del mouse passa sopra al controllo).
Il Trigger valuta questa condizione e applica la proprietà al verificarsi di essa.Se provate ad avviare l'applicazione (comprensiva, ovviamente, del codice mostrato nel paragrafo precedente) noterete che al passaggio del mouse sul pulsante viene modificato il colore di sfondo con un nuovo gradiente.
I Trigger sono molto potenti ed è possibile, ad esempio, valutare più condizioni contemporaneamente utilizzando un elemento MultiTriggers.
All'interno di questo elemento si definiscono le condizioni che devono essere soddisfatte e la proprietà (col relativo valore) che deve essere assegnata al controllo al verificarsi delle condizioni.
Il blocco di codice precedente può essere riscritto nel seguente modo:<Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" Value="True" /> <Condition Property="IsEnabled" Value="True" /> </MultiTrigger.Conditions> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0" Color="Black" /> <GradientStop Offset="0.5" Color="Gainsboro" /> <GradientStop Offset="1" Color="White" /> </LinearGradientBrush> </Setter.Value> </Setter> </MultiTrigger> </Style.Triggers>In questo modo, affinchè la proprietà Background del controllo venga modificata, devono essere soddisfatte due condizioni: che il pulsante sia abilitato (attributo IsEnabled) e che il puntatore del mouse passi sopra al controllo (attributo IsMouseOver).
A design-time, il risultato a video non cambia. In fase di esecuzione (avviando l'applicazione), potrete notare come lo sfondo venga applicato al verificarsi di entrambe le condizioni. Potete fare la prova del nove, impostando, ad esempio, il valore della proprietà IsEnabled su False. In questo modo, anche se il puntatore del mouse passerà sopra al controllo, lo sfondo non subirà modifiche.Ereditarietà degli stili
In Windows Presentation Foundation, gli stili supportano l'ereditarietà. E' possibile, infatti, definire uno stile di base e stili che da questo ereditano. L'unica differenza sta nel fatto che, nella definizione dello stile di base, non è possibile specificare con l'attributo TargetType il tipo di controllo cui lo stile è destinato, poiché l'oggetto destinatario dello stile può essere solo di tipo IFrameworkInputElement.Il seguente esempio può essere utile per capire meglio. Innanzitutto create un nuovo progetto vuoto per WPF, salvando o eliminando quello relativo al precedente esempio. Si definisce in primo luogo lo stile di base, tramite XAML, sempre all'interno delle risorse di un contenitore:
<Style x:Key="MyStyle"> <Setter Property="Button.FontFamily" Value="Courier New"/> <Setter Property="Button.FontSize" Value="12" /> <Setter Property="Button.Background" Value="Orange" /> <Setter Property="Button.Width" Value="150" /> <Setter Property="Button.Height" Value="75" /> </Style>Lo stile definisce alcune caratteristiche del font più lo sfondo e le dimensioni del pulsante.
Osservate come, in merito a quanto accennato all'inizio, sia necessario specificare in ogni elemento Setter il controllo cui appartiene la proprietà, invece di utilizzare un unico attributo TargetType.
Il secondo passaggio, è quello di definire lo stile ereditato:<Style x:Key="MyInheritedStyle" BasedOn="{StaticResource MyStyle}"> <Setter Property="Button.Foreground" Value="Blue" /> </Style>Come potete osservare, l'attributo BasedOn specifica lo stile da cui ereditare.
Abbiamo così tutte le proprietà dello stile di base, più il colore di primo piano del carattere aggiunto in questa fase.
Il codice per implementare un pulsante, poi, può essere il seguente:<Button Style="{StaticResource MyInheritedStyle}" Margin="10,10,10,10"> Ciao WPF! </Button>Vi basterà avviare l'applicazione per vedere un pulsante a sfondo arancio e colore di primo piano blu, come si vede nella seguente figura:
![]()
È possibile eseguire l'override di una proprietà degli stili di base semplicemente ridefinendola nello stile derivato.
Conclusioni
Come promesso, dopo aver appreso le conoscenze di base di WPF, iniziamo ad affrontare temi specifici. Le potenzialità di WPF sono enormi e ci consentono di dotare le nostre applicazioni di un aspetto grafico davvero accattivante; utilizzando adeguate tecniche di programmazione, inoltre, potremo riutilizzare il nostro lavoro e riadattarlo alle successive esigenze.
Come di consueto potete contattarmi al mio indirizzo visitare il mio blog.