Dans WPF, les concepts de DataTemplate, ControlTemplate et Style sont fondamentaux pour personnaliser l'apparence et le comportement des contrôles. Un ControlTemplate définit la srtucture visuelle interne d'un contrôle, tandis qu'un DataTemplate spécifie comment les données doivent être affichées. Les Style permettent de regrouper des propriétés et des déclencheurs pour appliquer des looks cohérents à plusieurs contrôles.
Relation entre ControlTemplate et DataTemplate
Un DataTemplate est souvent utilisé comme une sous-arborescence d'un ControlTemplate. Le ControlTemplate est responsable de l'apparence générale et du comportement d'un contrôle, utilisant souvent TemplateBinding pour lier ses propriétés à celles du contrôle parent. Par exemple, {TemplateBinding Background} lie la propriété Background du contrôle à celle définie dans le ControlTemplate.
Le DataTemplate, quant à lui, gère la présentation des données. Il utilise le mécanisme de Binding standard pour lier les propriétés des objets de données. Un ContentPresenter est couramment utilisé à l'intérieur d'un ControlTemplate pour afficher le contenu défini par un DataTemplate (souvent via la propriété ContentTemplate de ce ContentPresenter).
Application via Style
L'application de ControlTemplate et DataTemplate peut se faire individuellement sur chaque contrôle, ou de manière centralisée via un Style. Voici un exemple d'application d'un DataTemplate via un Style :
<Style x:Key="carDetailViewTemplate" TargetType="{x:Type UserControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<!-- Définition du DataTemplate ici -->
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Et pour un ControlTemplate appliqué à un TextBox :
<Style TargetType="{x:Type TextBox}">
<Setter property="Template">
<Setter.Value>
<ControlTemplate>
<!-- Définition du ControlTemplate ici -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
- ControlTemplate
Un ControlTemplate définit la structure visuelle d'un contrôle. Il peut être appliqué directement ou via un Style. Il comprend des éléments pour décrire l'apparence (l'arbre visuel) et des Triggers pour gérer les changements de comportement basés sur l'état du contrôle.
Voici un exemple de ControlTemplate dans un dictionnaire de ressources :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="CustomTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="5">
<ScrollViewer x:Name="PART_ContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="BorderBrush" Value="DodgerBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Pour utiliser ce style, vous pouvez le fusionner dans les ressources de votre fenêtre :
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Chemin/Vers/VotreDictionnaire.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<TextBox Style="{StaticResource CustomTextBoxStyle}"/>
</Grid>
TemplateBinding permet de lier les propriétés du contrôle hôte aux éléments du ControlTemplate. Il est possible d'utiliser des convertisseurs (Converter) pour transformer les données lors de la liaison.
- DataTemplate
Un DataTemplate définit l'apparence des données. Il est couramment utilisé dans les propriétés ContentTemplate, ItemTemplate (pour ItemsControl) et CellTemplate.
Les contrôles de la famille ContentControl incluent : Button, CheckBox, ComboBoxItem, Label, ListBoxItem, ListViewItem, RadioButton, ScrollViewer, UserControl, Window, ToolTip, et ButtonBase.
Un DataTemplate s'insère dans un ControlTemplate, souvent via un ContentPresenter.
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal">
<Grid>
<!-- Exemple de rendu visuel lié à une propriété 'Price' -->
<Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding Price}"/>
<TextBlock Text="{Binding Year}"/>
</Grid>
<TextBlock Text="{Binding Price}"/>
</StackPanel>
</Grid>
</DataTemplate>
Différences entre TemplateBinding et Binding
TemplateBindingest unidirectionnel et spécifiquement conçu pour la liaison au sein d'unControlTemplate, équivalent à{Binding RelativeSource={RelativeSource TemplatedParent}}.Bindingest plus flexible mais peut avoir un impact sur les performances.
- Style
Un Style permet de définir des ensembles réutilisables de propriétés et de déclecnheurs pour les contrôles. Il est composé principalement de Setter et Trigger.
3.1 Utilisation de Setter
Setter applique une valeur à une propriété spécifique d'un contrôle lorsque le style est appliqué.
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="24"/>
<Setter Property="Foreground" Value="Navy"/>
</Style>
</Window.Resources>
On peut aussi définir un ControlTemplate complet dans un Setter.
3.2 Utilisation de Trigger
Trigger permet de modifier les propriétés d'un contrôle lorsque certaines conditions sont remplies. Il existe plusieurs types de déclencheurs :
a. Trigger de base
Se déclenche lorsque la propriété spécifiée atteint la valeur indiquée.
<Style x:Key="CheckBoxStyle" TargetType="CheckBox">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="FontSize" Value="20"/>
<Setter Property="Foreground" Value="Orange"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- Application -->
<CheckBox Content="Option" Style="{StaticResource CheckBoxStyle}"/>
b. MultiTrigger
Se déclenche si plusieurs conditions sont remplies simultanément.
<Style x:Key="AdvancedCheckBoxStyle" TargetType="CheckBox">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsChecked" Value="true"/>
<Condition Property="Content" Value="yxz"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Foreground" Value="Red"/>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
c. DataTrigger
Se déclenche en fonction de la valeur d'une propriété liée à des données.
d. MultiDataTrigger
Se déclenche si plusieurs conditions basées sur des données sont remplies.
e. EventTrigger
Se déclenche à la suite d'un événement, souvent utilisé pour démarrer des animations.
<Style TargetType="Button" x:Key="AnimatedButtonStyle">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="15" Duration="0:0:0:2" Storyboard.TargetProperty="Width"/>
<DoubleAnimation To="15" Duration="0:0:0:2" Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<!-- Retour aux dimensions initiales -->
<DoubleAnimation Duration="0:0:0:2" Storyboard.TargetProperty="Width"/>
<DoubleAnimation Duration="0:0:0:2" Storyboard.TargetProperty="Height"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Résumé
- Les déclencehurs ne sont pas limités aux
Style; lesTemplateen possèdent également. - Les
Stylemodifient les propriétés existantes d'un contrôle, tandis que lesControlTemplatepeuvent altérer la structure interne (l'arbre visuel) pour des personnalisations plus profondes.
Exemples d'application
Bouton personnalisé via ControlTemplate
<Button Width="50" Height="50" Background="Red" Content="Test">
<Button.Template>
<ControlTemplate>
<Grid>
<Ellipse x:Name="ellipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Button.Height}" Fill="{TemplateBinding Button.Background}"/>
<ContentPresenter x:Name="contP" HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding Button.Content}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" Value="LightBlue" TargetName="ellipse"/>
<Setter Property="FontSize" Value="20" TargetName="contP"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Bouton personnalisé via Style
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border x:Name="border" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Button.Height}" Background="{TemplateBinding Button.Background}"/>
<ContentPresenter x:Name="contP" HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding Button.Content}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="Blue" TargetName="border"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Application -->
<Button Style="{StaticResource ButtonStyle1}" Width="100" Height="40" Content="Test" Background="Gray"/>