Comment créer un modèle pour un contrôle (WPF.NET)

Avec Windows Presentation Foundation (WPF), vous pouvez personnaliser la structure visuelle et le comportement d’un contrôle existant avec votre propre modèle réutilisable. Les modèles peuvent être appliqués globalement à votre application, fenêtres et pages, ou directement aux contrôles. La plupart des scénarios qui vous obligent à créer un contrôle peuvent être couverts par la création d’un modèle pour un contrôle existant.

Important

La documentation du Guide du bureau pour .NET 7 et .NET 6 est en cours de construction.

Dans cet article, vous allez explorer la création d’un nouveau ControlTemplateButton contrôle.

Quand créer un ControlTemplate

Les contrôles ont de nombreuses propriétés, telles que Background, Foregroundet FontFamily. Ces propriétés contrôlent différents aspects de l’apparence du contrôle, mais les modifications que vous pouvez apporter en définissant ces propriétés sont limitées. Par exemple, vous pouvez définir la Foreground propriété en bleu et FontStyle en italique sur un CheckBox. Lorsque vous souhaitez personnaliser l’apparence du contrôle au-delà de la définition des autres propriétés sur le contrôle, vous créez un ControlTemplate.

Dans la plupart des interfaces utilisateur, un bouton a la même apparence générale : un rectangle avec du texte. Si vous souhaitez créer un bouton arrondi, vous pouvez créer un contrôle qui hérite du bouton ou recréer la fonctionnalité du bouton. En outre, le nouveau contrôle utilisateur fournirait le visuel circulaire.

Vous pouvez éviter de créer de nouveaux contrôles en personnalisant la disposition visuelle d’un contrôle existant. Avec un bouton arrondi, vous créez une ControlTemplate disposition visuelle souhaitée.

En revanche, si vous avez besoin d’un contrôle avec de nouvelles fonctionnalités, différentes propriétés et de nouveaux paramètres, vous créez un nouveau UserControl.

Prérequis

Créez une application WPF et, dans MainWindow.xaml (ou une autre fenêtre de votre choix), définissez les propriétés suivantes sur l’élément< Window> :

Propriété Valeur
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Définissez le contenu de l’élément <Window> sur le code XAML suivant :

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

À la fin, le fichier MainWindow.xaml doit ressembler à ce qui suit :

<Window x:Class="IntroToStylingAndTemplating.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:IntroToStylingAndTemplating"
        mc:Ignorable="d"
        Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <StackPanel Margin="10">
        <Label>Unstyled Button</Label>
        <Button>Button 1</Button>
        <Label>Rounded Button</Label>
        <Button>Button 2</Button>
    </StackPanel>
</Window>

Si vous exécutez l’application, il se présente comme suit :

WPF window with two unstyled buttons

Créer un ControlTemplate

La façon la plus courante de déclarer un ControlTemplate est une ressource dans la Resources section dans un fichier XAML. Étant donné que les modèles sont des ressources, ils obéissent aux mêmes règles d’étendue que celles qui s’appliquent à toutes les ressources. En d’autres termes, lorsque vous déclarez un modèle affecte l’endroit où le modèle peut être appliqué. Par exemple, si vous déclarez le modèle dans l’élément racine de votre fichier XAML de définition d’application, le modèle peut être utilisé n’importe où dans votre application. Si vous définissez le modèle dans une fenêtre, seuls les contrôles de cette fenêtre peuvent utiliser le modèle.

Pour commencer, ajoutez un Window.Resources élément à votre fichier MainWindow.xaml :

<Window x:Class="IntroToStylingAndTemplating.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:IntroToStylingAndTemplating"
        mc:Ignorable="d"
        Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <Window.Resources>
        
    </Window.Resources>
    <StackPanel Margin="10">
        <Label>Unstyled Button</Label>
        <Button>Button 1</Button>
        <Label>Rounded Button</Label>
        <Button>Button 2</Button>
    </StackPanel>
</Window>

Créez un <ControlTemplate> avec les propriétés suivantes définies :

Propriété Valeur
x :Key roundbutton
TargetType Button

Ce modèle de contrôle est simple :

  • un élément racine pour le contrôle, a Grid
  • pour Ellipse dessiner l’apparence arrondie du bouton
  • a ContentPresenter pour afficher le contenu du bouton spécifié par l’utilisateur
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBinding

Lorsque vous créez un nouveau ControlTemplate, vous pouvez toujours utiliser les propriétés publiques pour modifier l’apparence du contrôle. L’extension de balisage TemplateBinding lie une propriété d’un élément qui se trouve dans la ControlTemplate propriété publique définie par le contrôle. Lorsque vous utilisez un TemplateBinding, vous activez les propriétés sur le contrôle pour agir en tant que paramètres pour le modèle. Autrement dit, quand une propriété sur un contrôle est définie, cette valeur est passée à l’élément qui a le TemplateBinding.

Ellipse

Notez que les propriétés et les Fill propriétés de l’élément< Ellipse> sont liées aux propriétés et Background aux propriétés du ForegroundStroke contrôle.

ContentPresenter

Un <élément ContentPresenter> est également ajouté au modèle. Étant donné que ce modèle est conçu pour un bouton, prenez en compte que le bouton hérite de ContentControl. Le bouton présente le contenu de l’élément. Vous pouvez définir n’importe quoi à l’intérieur du bouton, tel que du texte brut ou même un autre contrôle. Les deux boutons suivants sont valides :

<Button>My Text</Button>

<!-- and -->

<Button>
    <CheckBox>Checkbox in a button</CheckBox>
</Button>

Dans les deux exemples précédents, le texte et la boîte de réception case activée sont définis comme propriété Button.Content. Tout ce qui est défini comme contenu peut être présenté par le biais d’un <ContentPresenter>, c’est-à-dire ce que fait le modèle.

Si l’objet ControlTemplate est appliqué à un ContentControl type, tel qu’un Button, il ContentPresenter est recherché dans l’arborescence d’éléments. Si la ContentPresenter valeur est trouvée, le modèle lie automatiquement la propriété du Content contrôle à l’objet ContentPresenter.

Utiliser le modèle

Recherchez les boutons déclarés au début de cet article.

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
</StackPanel>

Définissez la propriété du Template deuxième bouton sur la roundbutton ressource :

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>

Si vous exécutez le projet et examinez le résultat, vous verrez que le bouton a un arrière-plan arrondi.

WPF window with one template oval button

Vous avez peut-être remarqué que le bouton n’est pas un cercle, mais qu’il est asymétrique. En raison du fonctionnement de l’élément <Ellipse> , il se développe toujours pour remplir l’espace disponible. Faites en sorte que le cercle soit uniforme en modifiant les propriétés et height les width propriétés du bouton sur la même valeur :

<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button Template="{StaticResource roundbutton}" Width="65" Height="65">Button 2</Button>
</StackPanel>

WPF window with one template circular button

Ajouter un déclencheur

Même si un bouton avec un modèle appliqué semble différent, il se comporte comme n’importe quel autre bouton. Si vous appuyez sur le bouton, l’événement Click se déclenche. Toutefois, vous avez peut-être remarqué que lorsque vous déplacez votre souris sur le bouton, les visuels du bouton ne changent pas. Ces interactions visuelles sont toutes définies par le modèle.

Avec les systèmes dynamiques d’événements et de propriétés que WPF fournit, vous pouvez regarder une propriété spécifique pour une valeur, puis restyle le modèle le cas échéant. Dans cet exemple, vous allez regarder la propriété du IsMouseOver bouton. Lorsque la souris est sur le contrôle, stylez l’Ellipse ><avec une nouvelle couleur. Ce type de déclencheur est appelé PropertyTrigger.

Pour que cela fonctionne, vous devez ajouter un nom à l’Ellipse <> que vous pouvez référencer. Donnez-lui le nom d’backgroundElement.

<Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />

Ensuite, ajoutez un nouveau Trigger à la collection ControlTemplate.Triggers . Le déclencheur surveille l’événement IsMouseOver pour la valeur true.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="true">

        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

Ensuite, ajoutez un <Setter> au <déclencheur> qui modifie la propriété Fill de l’Ellipse <> en nouvelle couleur.

<Trigger Property="IsMouseOver" Value="true">
    <Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
</Trigger>

Exécutez le projet . Notez que lorsque vous déplacez la souris sur le bouton, la couleur du <Ellipse> change.

mouse moves over WPF button to change the fill color

Utiliser un VisualState

Les états visuels sont définis et déclenchés par un contrôle. Par exemple, lorsque la souris est déplacée sur le contrôle, l’état CommonStates.MouseOver est déclenché. Vous pouvez animer les modifications de propriété en fonction de l’état actuel du contrôle. Dans la section précédente, un <PropertyTrigger> a été utilisé pour remplacer l’arrière-plan du bouton AliceBlue par le moment où la IsMouseOver propriété était true. Au lieu de cela, créez un état visuel qui anime la modification de cette couleur, ce qui offre une transition fluide. Pour plus d’informations sur VisualStates, consultez Styles et modèles dans WPF.

Pour convertir PropertyTrigger> en état visuel animé, commencez par supprimer l’élément <ControlTemplate.Triggers> de votre modèle.<

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

Ensuite, dans la racine Grid> du modèle de contrôle, ajoutez l’élément< VisualStateManager.VisualStateGroups> avec un <VisualStateGroup> pour CommonStates.< Définissez deux états et NormalMouseOver.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                </VisualState>
                <VisualState Name="MouseOver">
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

Toutes les animations définies dans un <VisualState> sont appliquées lorsque cet état est déclenché. Créez des animations pour chaque état. Les animations sont placées dans un <élément Storyboard> . Pour plus d’informations sur les storyboards, consultez Vue d’ensemble des storyboards.

  • Normal

    Cet état anime le remplissage ellipse, en le rétablissant dans la couleur du Background contrôle.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
            To="{TemplateBinding Background}"
            Duration="0:0:0.3"/>
    </Storyboard>
    
  • MouseOver

    Cet état anime la couleur ellipse Background à une nouvelle couleur : Yellow.

    <Storyboard>
        <ColorAnimation Storyboard.TargetName="backgroundElement" 
            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
            To="Yellow" 
            Duration="0:0:0.3"/>
    </Storyboard>
    

ControlTemplate <> doit maintenant ressembler à ce qui suit.

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="backgroundElement" 
                            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                            To="{TemplateBinding Background}"
                            Duration="0:0:0.3"/>
                    </Storyboard>
                </VisualState>
                <VisualState Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="backgroundElement" 
                            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" 
                            To="Yellow" 
                            Duration="0:0:0.3"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

Exécutez le projet . Notez que lorsque vous déplacez la souris sur le bouton, la couleur de l’Ellipse <> s’anime.

mouse moves over WPF button to change the fill color with a visual state

Étapes suivantes