Style

De Banane Atomic
Aller à la navigationAller à la recherche

Définition

Permet de changer les valeurs des propriétés des objets graphiques.
Équivalent du CSS pour WPF.

Une fois appliqué, un style ne peut être modifié (propriété IsSealed).

Syntaxe

Depuis les ressources

Xaml.png
<Window.Resources>
    <!-- Sans key, s'applique à toutes les TextBox -->
    <Style TargetType="TextBox">
        <Setter Property="Background" Value="Cyan" />
    </Style>
    <!-- Change la propriété Background des TextBox qui utilisent explicitement ce style -->
    <Style x:Key="tbxStyle1" TargetType="TextBox">
        <Setter Property="Background" Value="Red" />
    </Style>
</Window.Resources>

<TextBox Style="{StaticResource tbxStyle1}"></TextBox>

Directement dans le Control

Xaml.png
<ComboBox SelectedIndex="0">
    <ComboBox.Style>
        <Style TargetType="ComboBox">
            <Setter Property="Background" Value="Cyan" />
        </Style>
    </ComboBox.Style>
</ComboBox>

Héritage de style: BasedOn

Quand on définit le Style d'un Control, on écrase le Style précédemment définit:

Xaml.png
<xxx.Resources>
    <!-- On définit un style par défaut qui va s'appliquer à tous les boutons -->
    <Style TargetType="Button">
        <Setter Property="Background" Value="Cyan"/>
    </Style>
</xxx.Resources>

<!-- Ce bouton a bien son Background à Cyan -->
<Button Content="Button1" />

<!-- Ici on redéfinit le style pour changer le Foreground -->
<!-- Le style par défaut étant écrasé le Background est gris (style du système) -->
<Button Content="Button2">
    <Button.Style>
        <Style TargetType="Button" >
            <Setter Property="Foreground" Value="Beige"/>
        </Style>
    </Button.Style>
</Button>

La solution est d'hériter du Style par défaut:

Xaml.png
<Button Content="Button2">
    <Button.Style>
        <Style TargetType="Button" BasedOn="{StaticResource ResourceKey={x:Type Button}}">
            <Setter Property="Foreground" Value="Beige"/>
        </Style>
    </Button.Style>
</Button>

BasedOn et DynamicResource

On utilise donc StaticResource pour hériter du style par défaut. Mais StaticResource contraint la ressource à se trouver définie dans le dictionnaire de ressources.
Ce n'est pas toujours le cas par exemple si une application définit son thème par défaut dans App.xaml et utilise des Control importés depuis d'autres assemblages. Ces Control n'auront pas d'accès "statique" au style par défaut, et l'héritage ne se fait pas.
La solution serait d'utiliser un lien "dynamique" mais BasedOn ne supporte pas DynamiqueResource.
Il faut donc créer son propre MarkupExtension:

Csharp.svg
[MarkupExtensionReturnType(typeof(object))]
public class StaticApplicationResource : MarkupExtension
{
    public StaticApplicationResource(object resourceKey)
    {
        ResourceKey = resourceKey;
    }

    [ConstructorArgument("resourceKey")]
    public object ResourceKey { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (ResourceKey == null)
            return null;

        // On cherche dans App.xaml (et pas ailleurs) si on trouve la ressource.
        return System.Windows.Application.Current.TryFindResource(ResourceKey);
    }
}
Xaml.png
<Button Content="Button2">
    <Button.Style>
        <Style TargetType="Button" BasedOn="{my:StaticApplicationResource {x:Type Button}}"">
            <Setter Property="Foreground" Value="Beige"/>
        </Style>
    </Button.Style>
</Button>

Ordre de résolution entre les Style

Style order.png

Valeur par défaut

Csharp.svg
public static readonly DependencyProperty MyValueProperty =
    DependencyProperty.Register("MyValue", typeof(string), typeof(MyCustomControl), new UIPropertyMetadata("Valeur par défaut"));

Héritage

Xaml.png
<Grid local:MyCustomControl.MyValue="Héritage">
    <local:MyCustomControl />
</Grid>

Style par défaut

Xaml.png
<Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="MyValue" Value="Style par défaut" />

Style setters

Xaml.png
<local:MyCustomControl>
    <local:MyCustomControl.Style>
        <Style TargetType="local:MyCustomControl">
            <Setter Property="MyValue" Value="Style setters" />

Template triggers

Xaml.png
<local:MyCustomControl>
    <local:MyCustomControl.Template>
        <ControlTemplate TargetType="local:MyCustomControl">
            <ControlTemplate.Triggers>
                <Trigger Property="IsEnabled" Value="True">
                    <Setter Property="MyValue" Value="Template triggers" />

Style triggers

Xaml.png
<local:MyCustomControl>
    <local:MyCustomControl.Style>
        <Style TargetType="local:MyCustomControl">
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="True">
                    <Setter Property="MyValue" Value="Style triggers" />

Style implicite

Dans un dictionnaire de ressources, style sans clé qui s'applique à tous les TargetType.

Xaml.png
<!-- style sans clé qui s'applique à tous les TextBlock -->
<Style TargetType="{x:Type TextBlock}">
    <Setter Property="Foreground" Value="Blue" />
</Style>
La clé est en fait le TargetType.

TemplatedParent template properties

Xaml.png
<ContentControl x:Name="contentControl">
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">
            <local:MyCustomControl MyValue="TemplatedParent template properties" x:Name="myCustomControl">

Valeur locale

Csharp.svg
myCustomControl.MyValue = "Valeur locale";

Animation