App di stile con XAML
Le app .NET multipiattaforma dell'interfaccia utente dell'app (.NET MAUI) contengono spesso più controlli che hanno un aspetto identico. Ad esempio, un'app può avere più Label istanze con le stesse opzioni del tipo di carattere e opzioni di layout:
<Label Text="These labels"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
<Label Text="are not"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
<Label Text="using styles"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="18" />
In questo esempio ogni Label oggetto ha valori di proprietà identici per controllare l'aspetto del testo visualizzato da Label. Tuttavia, l'impostazione dell'aspetto di ogni singolo controllo può essere ripetitiva e soggetta a errori. È invece possibile creare uno stile che definisce l'aspetto e quindi applicato ai controlli necessari.
Introduzione agli stili
È possibile applicare uno stile a un'app usando la Style classe per raggruppare una raccolta di valori di proprietà in un oggetto che può quindi essere applicato a più elementi visivi. Ciò consente di ridurre il markup ripetitivo e di modificare più facilmente l'aspetto di un'app.
Anche se gli stili sono progettati principalmente per le app basate su XAML, possono anche essere creati in C#:
- Style Gli oggetti creati in XAML vengono in genere definiti in un ResourceDictionary oggetto assegnato alla
Resources
raccolta di un controllo, di una pagina o allaResources
raccolta dell'app. - Style Gli oggetti creati in C# vengono in genere definiti nella classe della pagina o in una classe accessibile a livello globale.
Scegliere dove definire un oggetto Style ha effetto su dove può essere usato:
- Style Le istanze definite a livello di controllo possono essere applicate solo al controllo e ai relativi elementi figlio.
- Style Le istanze definite a livello di pagina possono essere applicate solo alla pagina e ai relativi elementi figlio.
- Style Le istanze definite a livello di app possono essere applicate in tutta l'app.
Ogni Style oggetto contiene una raccolta di uno o più Setter oggetti, ognuno Setter con un Property
e un oggetto Value
. Property
è il nome della proprietà associabile dell'elemento a cui viene applicato lo stile e Value
è il valore applicato alla proprietà.
Ogni Style oggetto può essere esplicito o implicito:
- Un oggetto esplicito Style viene definito specificando un
TargetType
oggetto e unx:Key
valore e impostando la proprietà dell'elemento di Style destinazione sulx:Key
riferimento. Per altre informazioni, vedere Stili espliciti. - Un oggetto implicito Style viene definito specificando solo un oggetto
TargetType
. L'oggetto Style verrà quindi applicato automaticamente a tutti gli elementi di tale tipo. Tuttavia, le sottoclassi diTargetType
non hanno automaticamente l'oggetto Style applicato. Per altre informazioni, vedere Stili impliciti.
Quando si crea una classe Style, la proprietà TargetType
è sempre obbligatoria. L'esempio seguente mostra uno stile esplicito :
<Style x:Key="labelStyle" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
</Style>
Per applicare un Styleoggetto , l'oggetto di destinazione deve corrispondere VisualElement al TargetType
valore della proprietà di Style:
<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />
Gli stili inferiori nella gerarchia di visualizzazione hanno la precedenza su quelli definiti in alto. Ad esempio, l'impostazione di un Style oggetto che imposta Label.TextColor
su a livello di app verrà sottoposto a Red
override da uno stile a livello di pagina che imposta Label.TextColor
su Green
. Analogamente, uno stile a livello di pagina verrà sostituito da uno stile a livello di controllo. Inoltre, se Label.TextColor
è impostato direttamente su una proprietà di controllo, questa ha la precedenza su qualsiasi stile.
Gli stili non rispondono alle modifiche alle proprietà e rimangono invariati per la durata di un'app. Tuttavia, le app possono rispondere alle modifiche di stile in modo dinamico in fase di esecuzione usando risorse dinamiche. Per altre informazioni, vedere Stili dinamici.
Stili espliciti
Per creare un oggetto Style a livello di pagina, è necessario aggiungere un oggetto ResourceDictionary alla pagina e quindi includere una o più Style dichiarazioni in ResourceDictionary. Un Style oggetto viene reso esplicito assegnando alla relativa dichiarazione un x:Key
attributo, che fornisce una chiave descrittiva in ResourceDictionary. Gli stili espliciti devono quindi essere applicati a elementi visivi specifici impostandone Style le proprietà.
Nell'esempio ResourceDictionaryseguente vengono illustrati gli stili espliciti di una pagina e applicati agli oggetti della Label pagina:
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="labelRedStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Red" />
</Style>
<Style x:Key="labelGreenStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="labelBlueStyle"
TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="FontSize" Value="18" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Label Text="These labels"
Style="{StaticResource labelRedStyle}" />
<Label Text="are demonstrating"
Style="{StaticResource labelGreenStyle}" />
<Label Text="explicit styles,"
Style="{StaticResource labelBlueStyle}" />
<Label Text="and an explicit style override"
Style="{StaticResource labelBlueStyle}"
TextColor="Teal" />
</StackLayout>
</ContentPage>
In questo esempio, ResourceDictionary definisce tre stili impostati in modo esplicito sugli oggetti della Label pagina. Ognuno Style viene usato per visualizzare il testo in un colore diverso, impostando anche le dimensioni del carattere e le opzioni di layout orizzontale e verticale. Ogni Style oggetto viene applicato a un oggetto diverso Label impostandone Style le proprietà usando l'estensione di StaticResource
markup. Inoltre, mentre l'ultima Label ha un Style set su di esso, esegue anche l'override della TextColor
proprietà su un valore diverso Color .
Stili impliciti
Per creare un oggetto Style a livello di pagina, è necessario aggiungere un oggetto ResourceDictionary alla pagina e quindi includere una o più Style dichiarazioni in ResourceDictionary. Un Style oggetto viene reso implicito non specificando un x:Key
attributo. Lo stile verrà quindi applicato a negli elementi visivi dell'ambito TargetType
che corrispondono esattamente, ma non agli elementi derivati dal TargetType
valore.
L'esempio ResourceDictionarydi codice seguente mostra uno stile implicito in una pagina e applicato agli oggetti della Entry pagina:
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="Entry">
<Setter Property="HorizontalOptions" Value="Fill" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="BackgroundColor" Value="Yellow" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
</ContentPage.Resources>
<StackLayout>
<Entry Text="These entries" />
<Entry Text="are demonstrating" />
<Entry Text="implicit styles," />
<Entry Text="and an implicit style override"
BackgroundColor="Lime"
TextColor="Red" />
<local:CustomEntry Text="Subclassed Entry is not receiving the style" />
</StackLayout>
</ContentPage>
In questo esempio, ResourceDictionary definisce un singolo stile implicito impostato in modo implicito sugli oggetti della Entry pagina. Viene Style utilizzato per visualizzare il testo blu su uno sfondo giallo, impostando anche altre opzioni di aspetto. L'oggetto Style ResourceDictionary viene aggiunto alla pagina senza specificare un x:Key
attributo. Pertanto, l'oggetto Style viene applicato a tutti gli Entry oggetti in modo implicito, in quanto corrispondono esattamente alla TargetType
proprietà dell'oggetto Style . Tuttavia, l'oggetto Style non viene applicato all'oggetto CustomEntry
, che è un sottoclassato Entry. Inoltre, il quarto Entry esegue l'override delle BackgroundColor
proprietà e TextColor
dello stile a valori diversi Color .
Applicare uno stile ai tipi derivati
La Style.ApplyToDerivedTypes
proprietà consente di applicare uno stile ai controlli derivati dal tipo di base a cui fa riferimento la TargetType
proprietà . Pertanto, l'impostazione di questa proprietà su consente a true
un singolo stile di specificare più tipi, purché i tipi derivino dal tipo di base specificato nella TargetType
proprietà .
L'esempio seguente mostra uno stile implicito che imposta il colore di sfondo delle Button istanze su rosso:
<Style TargetType="Button"
ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="Red" />
</Style>
L'inserimento di questo stile in un livello ResourceDictionary di pagina comporta l'applicazione a tutti gli oggetti nella pagina e anche a tutti i Button controlli che derivano da Button. Tuttavia, se la ApplyToDerivedTypes
proprietà è rimasta non impostata, lo stile verrà applicato solo agli Button oggetti .
Stili globali
Gli stili possono essere definiti a livello globale aggiungendoli al dizionario risorse dell'app. Questi stili possono quindi essere utilizzati in un'app e consentono di evitare la duplicazione dello stile tra pagine e controlli.
L'esempio seguente mostra un Style oggetto definito a livello di app:
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Styles"
x:Class="Styles.App">
<Application.Resources>
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="HorizontalOptions"
Value="Center" />
<Setter Property="VerticalOptions"
Value="CenterAndExpand" />
<Setter Property="BorderColor"
Value="Lime" />
<Setter Property="CornerRadius"
Value="5" />
<Setter Property="BorderWidth"
Value="5" />
<Setter Property="WidthRequest"
Value="200" />
<Setter Property="TextColor"
Value="Teal" />
</Style>
</Application.Resources>
</Application>
In questo esempio, ResourceDictionary definisce un singolo stile esplicito , buttonStyle
, che verrà usato per impostare l'aspetto degli Button oggetti.
Nota
Gli stili globali possono essere espliciti o impliciti.
Nell'esempio seguente viene illustrata una pagina che utilizza gli buttonStyle
oggetti della Button pagina:
<ContentPage ...>
<StackLayout>
<Button Text="These buttons"
Style="{StaticResource buttonStyle}" />
<Button Text="are demonstrating"
Style="{StaticResource buttonStyle}" />
<Button Text="application styles"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage>
Ereditarietà dello stile
Gli stili possono ereditare da altri stili per ridurre la duplicazione e abilitare il riutilizzo. Questa operazione viene ottenuta impostando la Style.BasedOn
proprietà su un oggetto esistente Style. In XAML questo risultato può essere ottenuto impostando la BasedOn
proprietà su un'estensione StaticResource
di markup che fa riferimento a un oggetto creato Stylein precedenza.
Gli stili che ereditano da uno stile di base possono includere Setter istanze per le nuove proprietà o usarli per eseguire l'override dei setter dallo stile di base. Inoltre, gli stili che ereditano da uno stile di base devono avere come destinazione lo stesso tipo o un tipo che deriva dal tipo di destinazione dello stile di base. Ad esempio, se uno stile di base ha come View destinazione oggetti, gli stili basati sullo stile di base possono essere destinati a View oggetti o tipi che derivano dalla View classe , ad esempio Label e Button .
Uno stile può ereditare solo da stili allo stesso livello, o superiore, nella gerarchia di visualizzazione. Ciò significa che:
- Uno stile a livello di app può ereditare solo da altri stili a livello di app.
- Uno stile a livello di pagina può ereditare da stili a livello di app e altri stili a livello di pagina.
- Uno stile a livello di controllo può ereditare da stili a livello di app, stili a livello di pagina e altri stili a livello di controllo.
L'esempio seguente mostra l'ereditarietà esplicita dello stile:
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ContentPage.Resources>
<StackLayout>
<StackLayout.Resources>
<Style x:Key="labelStyle"
TargetType="Label"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Teal" />
</Style>
<Style x:Key="buttonStyle"
TargetType="Button"
BasedOn="{StaticResource baseStyle}">
<Setter Property="BorderColor" Value="Lime" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="BorderWidth" Value="5" />
<Setter Property="WidthRequest" Value="200" />
<Setter Property="TextColor" Value="Teal" />
</Style>
</StackLayout.Resources>
<Label Text="This label uses style inheritance"
Style="{StaticResource labelStyle}" />
<Button Text="This button uses style inheritance"
Style="{StaticResource buttonStyle}" />
</StackLayout>
</ContentPage>
In questo esempio, gli baseStyle
oggetti di View destinazione e impostano le HorizontalOptions
proprietà e VerticalOptions
. l'oggetto baseStyle
non è impostato direttamente su alcun controllo. Al contrario, labelStyle
e buttonStyle
ereditano da esso, impostando valori di proprietà associabili aggiuntivi. Gli labelStyle
oggetti e buttonStyle
vengono quindi impostati su e Label Button.
Importante
Uno stile implicito può essere derivato da uno stile esplicito, ma non è possibile derivare uno stile esplicito da uno stile implicito.
Stili dinamici
Gli stili non rispondono alle modifiche alle proprietà e rimangono invariati per la durata di un'app. Ad esempio, dopo aver assegnato un Style oggetto a un elemento visivo, se uno degli Setter oggetti viene modificato, rimosso o aggiunto nuovo Setter , le modifiche non verranno applicate all'elemento visivo. Tuttavia, le app possono rispondere alle modifiche di stile in modo dinamico in fase di esecuzione usando risorse dinamiche.
L'estensione DynamicResource
di markup è simile all'estensione StaticResource
di markup in che entrambi usano una chiave del dizionario per recuperare un valore da un oggetto ResourceDictionary. Tuttavia, mentre StaticResource
esegue una singola ricerca nel dizionario, mantiene DynamicResource
un collegamento alla chiave del dizionario. Pertanto, se la voce del dizionario associata alla chiave viene sostituita, la modifica viene applicata all'elemento visivo. In questo modo è possibile apportare modifiche allo stile di runtime in un'app.
L'esempio seguente mostra gli stili dinamici :
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="PlaceholderColor" Value="Blue" />
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
<Setter Property="FontAttributes" Value="None" />
<Setter Property="PlaceholderColor" Value="Green" />
</Style>
</ContentPage.Resources>
<StackLayout>
<SearchBar Placeholder="SearchBar demonstrating dynamic styles"
Style="{DynamicResource blueSearchBarStyle}" />
</StackLayout>
</ContentPage>
In questo esempio l'oggetto SearchBar usa l'estensione DynamicResource
di markup per impostare un Style oggetto denominato blueSearchBarStyle
. Può SearchBar quindi aggiornare la definizione Style nel codice:
Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];
In questo esempio la blueSearchBarStyle
definizione viene aggiornata per usare i valori della greenSearchBarStyle
definizione. Quando questo codice viene eseguito, SearchBar verrà aggiornato per utilizzare gli Setter oggetti definiti in greenSearchBarStyle
.
Ereditarietà dello stile dinamico
La derivazione di uno stile da uno stile dinamico non può essere ottenuta usando la Style.BasedOn
proprietà . La classe include invece Style la BaseResourceKey
proprietà , che può essere impostata su una chiave del dizionario il cui valore potrebbe cambiare dinamicamente.
L'esempio seguente mostra l'ereditarietà dello stile dinamico :
<ContentPage ...>
<ContentPage.Resources>
<Style x:Key="baseStyle"
TargetType="View">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style x:Key="blueSearchBarStyle"
TargetType="SearchBar"
BasedOn="{StaticResource baseStyle}">
<Setter Property="FontAttributes" Value="Italic" />
<Setter Property="TextColor" Value="Blue" />
</Style>
<Style x:Key="greenSearchBarStyle"
TargetType="SearchBar">
<Setter Property="FontAttributes" Value="None" />
<Setter Property="TextColor" Value="Green" />
</Style>
<Style x:Key="tealSearchBarStyle"
TargetType="SearchBar"
BaseResourceKey="blueSearchBarStyle">
<Setter Property="BackgroundColor" Value="Teal" />
<Setter Property="CancelButtonColor" Value="White" />
</Style>
</ContentPage.Resources>
<StackLayout>
<SearchBar Text="SearchBar demonstrating dynamic style inheritance"
Style="{StaticResource tealSearchBarStyle}" />
</StackLayout>
</ContentPage>
In questo esempio l'oggetto SearchBar usa l'estensione StaticResource
di markup per fare riferimento a un Style oggetto denominato tealSearchBarStyle
. In questo modo Style vengono impostate alcune proprietà aggiuntive e viene utilizzata la BaseResourceKey
proprietà per fare riferimento blueSearchBarStyle
a . L'estensione DynamicResource
di markup non è necessaria perché tealSearchBarStyle
non verrà modificata, ad eccezione dell'estensione Style da cui deriva. Pertanto, tealSearchBarStyle
mantiene un collegamento a blueSearchBarStyle
e viene aggiornato quando lo stile di base cambia.
La blueSearchBarStyle
definizione può essere aggiornata nel codice:
Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];
In questo esempio la blueSearchBarStyle
definizione viene aggiornata per usare i valori della greenSearchBarStyle
definizione. Quando questo codice viene eseguito, SearchBar verrà aggiornato per utilizzare gli Setter oggetti definiti in greenSearchBarStyle
.
Classi di stile
Le classi di stile consentono l'applicazione di più stili a un controllo, senza ricorrere all'ereditarietà dello stile.
È possibile creare una classe di stile impostando la Class
proprietà su un Style string
oggetto che rappresenta il nome della classe. Il vantaggio offerto, oltre a definire uno stile esplicito usando l'attributo x:Key
, è che più classi di stile possono essere applicate a un oggetto VisualElement.
Importante
Più stili possono condividere lo stesso nome di classe, purché abbiano come destinazione tipi diversi. In questo modo, più classi di stile, denominate in modo identico, vengono destinate a tipi diversi.
L'esempio seguente mostra tre BoxView classi di stile e una VisualElement classe di stile:
<ContentPage ...>
<ContentPage.Resources>
<Style TargetType="BoxView"
Class="Separator">
<Setter Property="BackgroundColor"
Value="#CCCCCC" />
<Setter Property="HeightRequest"
Value="1" />
</Style>
<Style TargetType="BoxView"
Class="Rounded">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="10" />
</Style>
<Style TargetType="BoxView"
Class="Circle">
<Setter Property="BackgroundColor"
Value="#1FAECE" />
<Setter Property="WidthRequest"
Value="100" />
<Setter Property="HeightRequest"
Value="100" />
<Setter Property="HorizontalOptions"
Value="Start" />
<Setter Property="CornerRadius"
Value="50" />
</Style>
<Style TargetType="VisualElement"
Class="Rotated"
ApplyToDerivedTypes="true">
<Setter Property="Rotation"
Value="45" />
</Style>
</ContentPage.Resources>
</ContentPage>
In questo esempio, le Separator
classi di stile , Rounded
e Circle
impostano le proprietà su BoxView valori specifici. La Rotated
classe style ha un TargetType
di VisualElement, il che significa che può essere applicato solo alle VisualElement istanze. Tuttavia, la proprietà ApplyToDerivedTypes
è impostata su true
, che garantisce che possa essere applicata a tutti i controlli che derivano da VisualElement, ad esempio BoxView. Per altre informazioni sull'applicazione di uno stile a un tipo derivato, vedere Applicare uno stile ai tipi derivati.
Le classi di stile possono essere utilizzate impostando la StyleClass
proprietà del controllo , che è di tipo IList<string>
, su un elenco di nomi di classi di stile. Le classi di stile verranno applicate, purché il tipo del controllo corrisponda a quello TargetType
delle classi di stile.
L'esempio seguente mostra tre BoxView istanze, ognuna impostata su classi di stile diverse:
<ContentPage ...>
<ContentPage.Resources>
...
</ContentPage.Resources>
<StackLayout>
<BoxView StyleClass="Separator" />
<BoxView WidthRequest="100"
HeightRequest="100"
HorizontalOptions="Center"
StyleClass="Rounded, Rotated" />
<BoxView HorizontalOptions="Center"
StyleClass="Circle" />
</StackLayout>
</ContentPage>
In questo esempio, il primo BoxView viene stilizzato come separatore di riga, mentre il terzo BoxView è circolare. Il secondo BoxView ha due classi di stile applicate, che assegnano angoli arrotondati e ruotano 45 gradi:
Importante
È possibile applicare più classi di stile a un controllo perché la StyleClass
proprietà è di tipo IList<string>
. In questo caso, le classi di stile vengono applicate in ordine crescente di elenco. Pertanto, quando più classi di stile impostano proprietà identiche, la proprietà nella classe di stile che si trova nella posizione di elenco più alta avrà la precedenza.