Partager via


Appliquer un style aux applications en utilisant XAML

Les applications .NET Multi-Plateform App UI (.NET MAUI) contiennent souvent plusieurs contrôles qui ont une apparence identique. Par exemple, une application peut avoir plusieurs instances de Label qui ont les mêmes options de police et de disposition :

<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" />

Dans cet exemple, chaque objet Label a des valeurs de propriété identiques pour contrôler l’apparence du texte affiché par le Label. Toutefois, la définition de l’apparence de chaque contrôle individuellement peut être répétitive et sujette aux erreurs. Au lieu de cela, un style peut être créé pour définir l'apparence, puis appliqué aux contrôles requis.

Présentation des styles

Une application peut être mise en forme à l’aide de la classe Style pour regrouper une collection de valeurs de propriété dans un objet qui peut ensuite être appliqué à plusieurs éléments visuels. Cela permet de réduire les marques répétitives et permet à l’apparence d’une application d’être plus facilement modifiée.

Bien que les styles soient conçus principalement pour les applications XAML, ils peuvent également être créés en C# :

  • Les objets Style créés en XAML sont généralement définis dans un ResourceDictionary affecté à la collection Resources d’un contrôle, d’une page ou à la collection Resources de l’application.
  • Les objets Style créés en C# sont généralement définis dans la classe de la page ou dans une classe accessible globalement.

Le fait de choisir où définir Style impacte l’emplacement où il peut être utilisé :

  • Les instances de Style définies au niveau du contrôle ne peuvent être appliquées qu’au contrôle et à ses enfants.
  • Les instances de Style définies au niveau de la page ne peuvent être appliquées qu’à la page et à ses enfants.
  • Les instances de Style définies au niveau de l’application peuvent être appliquées dans l’ensemble de l’application.

Chaque objet Style contient une collection d’un ou plusieurs objets Setter, chaque Setter ayant un Property et un Value. Property est le nom de la propriété pouvant être liée de l’élément auquel le style est appliqué, et Value est la valeur qui est appliquée à la propriété.

Chaque objet Style peut être explicite ou implicite :

  • Un objet Style explicite est défini en spécifiant un TargetType et une valeur x:Key, et en définissant la propriété Style de l’élément cible sur la référence x:Key. Pour plus d’informations, consultez Styles explicites.
  • Un objet Style implicite est défini en spécifiant uniquement un TargetType. L’objet Style sera ensuite automatiquement appliqué à tous les éléments de ce type. Toutefois, les sous-classes du TargetType n’ont pas automatiquement le Style appliqué. Pour plus d’informations, consultez Styles implicites.

Durant la création de Style, la propriété TargetType est toujours obligatoire. L’exemple suivant montre un style explicite :

<Style x:Key="labelStyle" TargetType="Label">
    <Setter Property="HorizontalOptions" Value="Center" />
    <Setter Property="VerticalOptions" Value="Center" />
    <Setter Property="FontSize" Value="18" />
</Style>

Pour appliquer un Style, l’objet cible doit être un VisualElement qui correspond à la valeur de propriété TargetType du Style :

<Label Text="Demonstrating an explicit style" Style="{StaticResource labelStyle}" />

Les styles inférieurs dans la hiérarchie d’affichage sont prioritaires sur ceux définis plus haut. Par exemple, la définition d’un Style qui définit Label.TextColor sur Red au niveau de l’application sera remplacée par un style au niveau de la page qui définit Label.TextColor sur Green. De même, un style de niveau page sera remplacé par un style de niveau de contrôle. En outre, si Label.TextColor est défini directement sur une propriété de contrôle, cela est prioritaire sur tous les styles.

Les styles ne répondent pas aux modifications de propriété et restent inchangés pendant la durée d’une application. Toutefois, les applications peuvent répondre aux modifications de style dynamiquement au moment de l’exécution à l’aide de ressources dynamiques. Pour plus d'informations, consultez la page Styles dynamiques.

Styles explicites

Pour créer un Style au niveau de la page, un ResourceDictionary doit être ajouté à la page, puis une ou plusieurs déclarations Style peuvent être incluses dans le ResourceDictionary. Un Style est rendu explicite en donnant à sa déclaration un attribut x:Key, qui lui donne une clé descriptive dans le ResourceDictionary. Les styles explicites doivent ensuite être appliqués à des éléments visuels spécifiques en définissant leurs propriétés Style.

L’exemple suivant montre des styles explicites dans le ResourceDictionary d’une page et appliqués aux objets Label de la page :

<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>

Dans cet exemple, le ResourceDictionary définit trois styles définis explicitement sur les objets Label de la page. Chaque Style est utilisé pour afficher du texte dans une couleur différente, tout en définissant également la taille de police et les options de disposition horizontale et verticale. Chaque Style est appliqué à un Label différent en définissant ses propriétés Style à l’aide de l’extension de balisage StaticResource. En outre, alors que le dernier Label a un Style défini, il remplace également la propriété TextColor par une valeur Color différente.

Styles implicites

Pour créer un Style au niveau de la page, un ResourceDictionary doit être ajouté à la page, puis une ou plusieurs déclarations Style peuvent être incluses dans le ResourceDictionary. Un Style est rendu implicite en ne spécifiant pas d’attribut x:Key. Le style sera alors appliqué aux éléments visuels dans l’étendue qui correspondent exactement au TargetType, mais pas aux éléments dérivés de la valeur TargetType.

L’exemple de code suivant montre un style implicite dans le ResourceDictionaryd’une page et appliqué aux objets Entry de la page :

<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>

Dans cet exemple, le ResourceDictionary définit un style implicite qui est implicitement défini sur les objets Entry de la page. Le Style est utilisé pour afficher du texte bleu sur un arrière-plan jaune, tout en définissant d’autres options d’apparence. Le Style est ajouté au ResourceDictionary de la page sans spécifier d’attribut x:Key. Par conséquent, le Style est appliqué implicitement à tous les objets Entry, car ils correspondent exactement à la propriété TargetType du Style. Toutefois, le Style n’est pas appliqué à l’objet CustomEntry, qui est un Entrysous-classé. En outre, le quatrième Entry remplace les propriétés BackgroundColor et TextColor du style à différentes valeurs Color.

Appliquer un style aux types dérivés

La propriété Style.ApplyToDerivedTypes permet d’appliquer un style aux contrôles dérivés du type de base référencé par la propriété TargetType. Par conséquent, la définition de cette propriété sur true permet à un style unique de cibler plusieurs types, à condition que les types dérivent du type de base spécifié dans la propriété TargetType.

L’exemple suivant montre un style implicite qui définit la couleur d’arrière-plan des instances de Button sur rouge :

<Style TargetType="Button"
       ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor"
            Value="Red" />
</Style>

Le placement de ce style dans un ResourceDictionary au niveau de la page entraîne son application à tous les objets Button de la page, ainsi qu’à tous les contrôles dérivés de Button. Toutefois, si la propriété ApplyToDerivedTypes n’a pas été définie, le style n’est appliqué qu’aux objets Button.

Styles globaux

Les styles peuvent être définis globalement en les ajoutant au dictionnaire de ressources de l’application. Ces styles peuvent ensuite être consommés dans une application et vous aider à éviter la duplication de style entre les pages et les contrôles.

L’exemple suivant montre un Style défini au niveau de l’application :


<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>

Dans cet exemple, le ResourceDictionary définit un seul style explicite, buttonStyle, qui sera utilisé pour définir l’apparence des objets Button.

Remarque

Les styles globaux peuvent être explicites ou implicites.

L’exemple suivant montre une page consommant le buttonStyle sur les objets Button de la page :

<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>

Héritage de style

Les styles peuvent hériter d’autres styles pour réduire la duplication et permettre la réutilisation. Pour ce faire, définissez la propriété Style.BasedOn sur une Style existante. En XAML, cela peut être obtenu en définissant la propriété BasedOn sur une extension de balisage StaticResource qui fait référence à un Stylecréé précédemment.

Les styles qui héritent d’un style de base peuvent inclure des Setter instances pour de nouvelles propriétés ou les utiliser pour remplacer les accesseurs du style de base. En outre, les styles qui héritent d’un style de base doivent cibler le même type, ou un type qui dérive du type ciblé par le style de base. Par exemple, si un style de base cible des objets View, les styles basés sur le style de base peuvent cibler des objets View ou des types dérivés de la classe View, tels que des objets Label et Button.

Un style peut hériter uniquement des styles au même niveau, ou au niveau supérieur, dans la hiérarchie d’affichage. Cela signifie que :

  • Un style de niveau de l’application ne peut hériter que d’autres styles au niveau de l’application.
  • Un style au niveau de la page peut hériter de styles au niveau de l'application et d'autres styles au niveau de la page.
  • Un style au niveau du contrôle peut hériter des styles au niveau de l’application, des styles au niveau de la page et d'autres styles au niveau du contrôle.

L’exemple suivant montre l’héritage de style explicite :

<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>

Dans cet exemple, le baseStyle cible les objets View et définit les propriétés HorizontalOptions et VerticalOptions. Le baseStyle n’est pas défini directement sur les contrôles. Au lieu de cela, labelStyle et buttonStyle héritent de celui-ci, en définissant d’autres valeurs de propriété pouvant être liées. Les objets labelStyle et buttonStyle sont ensuite définis sur un Label et Button.

Important

Un style implicite peut être dérivé d’un style explicite, mais un style explicite ne peut pas être dérivé d’un style implicite.

Styles dynamiques

Les styles ne répondent pas aux modifications de propriété et restent inchangés pendant la durée d’une application. Par exemple, après avoir affecté un Style à un élément visuel, si l’un des objets Setter est modifié, supprimé ou ajouté à un nouveau Setter ajouté, les modifications ne seront pas appliquées à l’élément visuel. Toutefois, les applications peuvent répondre aux modifications de style dynamiquement au moment de l’exécution à l’aide de ressources dynamiques.

L’extension de balisage DynamicResource est similaire à l’extension de balisage StaticResource dans laquelle les deux utilisent une clé de dictionnaire pour extraire une valeur d’un ResourceDictionary. Toutefois, alors que le StaticResource effectue une seule recherche dans le dictionnaire, le DynamicResource conserve un lien vers la clé du dictionnaire. Par conséquent, si l’entrée de dictionnaire associée à la clé est remplacée, la modification est appliquée à l’élément visuel. Cela permet d’apporter des modifications de style à l’exécution dans une application.

L’exemple suivant montre des styles dynamiques :

<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>

Dans cet exemple, l’objet SearchBar utilise l’extension de balisage DynamicResource pour définir un Style nommé blueSearchBarStyle. Le SearchBar peut ensuite avoir sa définition de Style mise à jour dans le code :

Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];

Dans cet exemple, la définition blueSearchBarStyle est mise à jour pour utiliser les valeurs de la définition greenSearchBarStyle. Lorsque ce code est exécuté, le SearchBar sera mis à jour pour utiliser les objets Setter définis dans greenSearchBarStyle.

Héritage de style dynamique

La dérivation d’un style dynamique ne peut pas être obtenue à l’aide de la propriété Style.BasedOn. Au lieu de cela, la classe Style inclut la propriété BaseResourceKey, qui peut être définie sur une clé de dictionnaire dont la valeur peut changer dynamiquement.

L’exemple suivant montre l’héritage de style dynamique :

<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>

Dans cet exemple, l’objet SearchBar utilise l’extension de balisage StaticResource pour référencer un Style nommé tealSearchBarStyle. Cette Style définit certaines propriétés supplémentaires et utilise la propriété BaseResourceKey pour référencer blueSearchBarStyle. L’extension de balisage DynamicResource n’est pas nécessaire, car tealSearchBarStyle ne change pas, à l’exception de la Style dont elle dérive. Par conséquent, tealSearchBarStyle conserve un lien vers blueSearchBarStyle et est mis à jour lorsque le style de base change.

La définition blueSearchBarStyle peut être mise à jour dans le code :

Resources["blueSearchBarStyle"] = Resources["greenSearchBarStyle"];

Dans cet exemple, la définition blueSearchBarStyle est mise à jour pour utiliser les valeurs de la définition greenSearchBarStyle. Lorsque ce code est exécuté, le SearchBar sera mis à jour pour utiliser les objets Setter définis dans greenSearchBarStyle.

Classes de style

Les classes de style permettent à plusieurs styles d’être appliqués à un contrôle, sans recourir à l’héritage de style.

Une classe de style peut être créée en définissant la propriété Class sur un Style vers un string qui représente le nom de la classe. L’avantage que cela offre, par rapport à la définition d’un style explicite en utilisant l’attribut x:Key, est que plusieurs classes de style peuvent être appliquées à un VisualElement.

Important

Plusieurs styles peuvent partager le même nom de classe, à condition qu’ils ciblent différents types. Cela permet à plusieurs classes de style, qui sont identiquement nommées, de cibler différents types.

L’exemple suivant montre trois classes de style BoxView et une classe de style VisualElement :

<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>

Dans cet exemple, les classes de style Separator, Roundedet Circle définissent chaque propriété BoxView sur des valeurs spécifiques. La classe de style Rotated a une TargetType de VisualElement, ce qui signifie qu’elle ne peut être appliquée qu’aux instances de VisualElement. Toutefois, sa propriété ApplyToDerivedTypes est définie sur true, ce qui garantit qu’elle peut être appliquée à tous les contrôles dérivés de VisualElement, tels que BoxView. Pour plus d’informations sur l’application d’un style à un type dérivé, consultez Appliquer un style à des types dérivés.

Les classes de style peuvent être consommées en définissant la propriété StyleClass du contrôle, qui est de type IList<string>, sur une liste de noms de classes de style. Les classes de style sont appliquées, à condition que le type du contrôle corresponde à la TargetType des classes de style.

L’exemple suivant montre trois instances de BoxView, chacune définie sur différentes classes de style :

<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>    

Dans cet exemple, le premier BoxView est présenté comme séparateur de ligne, tandis que le troisième BoxView est circulaire. Le deuxième BoxView a deux classes de style appliquées à celui-ci, ce qui lui donne des angles arrondis et le fait pivoter de 45 degrés :

Capture d’écran de BoxViews avec des classes de style.

Important

Plusieurs classes de style peuvent être appliquées à un contrôle, car la propriété StyleClass est de type IList<string>. Lorsque cela se produit, les classes de style sont appliquées dans l’ordre croissant de la liste. Par conséquent, lorsque plusieurs classes de style définissent des propriétés identiques, la propriété de la classe de style qui se trouve dans la position la plus élevée de la liste prendra le dessus.