Aller au contenu

Style

De Banane Atomic

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

<Window.Resources>
    <!-- Style implicite (sans key), s'applique à toutes les types 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

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

Héritage de style: BasedOn

BasedOn permet d'hériter d'un autre style afin de l'enrichir.

<xxx.Resources>
    <!-- On hérite du style par défaut des boutons -->
    <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
        <Setter Property="Background" Value="Blue" />
    </Style>

    <!-- On hérite du style CustomButtonStyle -->
    <Style TargetType="Button" BasedOn="{StaticResource CustomButtonStyle}">
        <Setter Property="Background" Value="Blue" />
    </Style>
</xxx.Resources>

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:

[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);
    }
}
<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

Erreur lors de la création de la vignette : /bin/bash: /usr/bin/convert: No such file or directory Error code: 127

Valeur par défaut

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

Héritage

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

Style par défaut

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

Style setters

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

Template triggers

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

Style triggers

<local:MyCustomControl>
    <local:MyCustomControl.Style>
        <Style TargetType="local:MyCustomControl">
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Visibility" Value="Collapsed" />

Style implicite

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

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

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

Valeur locale

myCustomControl.MyValue = "Valeur locale";

Animation