Condividi tramite


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 alla Resources 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 esplicitoStyle viene definito specificando un TargetType oggetto e un x:Key valore e impostando la proprietà dell'elemento di Style destinazione sul x:Key riferimento. Per altre informazioni, vedere Stili espliciti.
  • Un oggetto implicitoStyle viene definito specificando solo un oggetto TargetType. L'oggetto Style verrà quindi applicato automaticamente a tutti gli elementi di tale tipo. Tuttavia, le sottoclassi di TargetType 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 StyleResourceDictionary 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 LabelButton.

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 blueSearchBarStylea . 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 Stylestring 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 Separatorclassi di stile , Roundede 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:

Screenshot of BoxViews styled with style classes.

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.