Utilisation de DataTemplate, ControlTemplate et Style dans WPF

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

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

  • TemplateBinding est unidirectionnel et spécifiquement conçu pour la liaison au sein d'un ControlTemplate, équivalent à {Binding RelativeSource={RelativeSource TemplatedParent}}.
  • Binding est plus flexible mais peut avoir un impact sur les performances.
  1. 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 ; les Template en possèdent également.
  • Les Style modifient les propriétés existantes d'un contrôle, tandis que les ControlTemplate peuvent 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"/>

Étiquettes: WPF XAML ControlTemplate DataTemplate Style

Publié le 15 juillet à 08h05