Erstellen einer Vorlage für ein Steuerelement (WPF.NET)

Mit Windows Presentation Foundation (WPF) können Sie die visuelle Struktur und das Verhalten eines vorhandenen Steuerelements mit Ihrer eigenen wiederverwendbaren Vorlage anpassen. Vorlagen können global auf Anwendungen, Fenster und Seiten oder direkt auf Steuerelemente angewendet werden. Die meisten Szenarien, in denen Sie ein neues Steuerelement erstellen müssen, können stattdessen durch Erstellen einer neuen Vorlage für ein vorhandenes Steuerelement abgedeckt werden.

Wichtig

Der Desktopleitfaden zu .NET 7 und .NET 6 ist in Bearbeitung.

In diesem Artikel erfahren Sie, wie Sie eine neue ControlTemplate für das Button-Steuerelement erstellen.

Wann sollte eine ControlTemplate erstellt werden?

Steuerelemente verfügen über viele Eigenschaften, etwa über Background, Foreground und FontFamily. Diese Eigenschaften steuern verschiedene Aspekte der Darstellung des Steuerelements, aber die Änderungen, die Sie vornehmen können, indem Sie diese Eigenschaften festlegen, sind eingeschränkt. Beispielsweise können Sie die Foreground-Eigenschaft auf „Blue“ festlegen und FontStyle für ein CheckBox-Element auf kursiv. Sie erstellen eine ControlTemplate, wenn Sie die Darstellung des Steuerelements über das hinaus ändern möchten, was durch das Festlegen anderer Steuerelementeigenschaften möglich ist.

In den meisten Benutzeroberflächen weis eine Schaltfläche dieselbe allgemeine Darstellung auf: ein Rechteck mit Text. Wenn Sie eine abgerundete Schaltfläche erstellen möchten, können Sie ein neues Steuerelement erstellen, das von der Schaltfläche erbt oder die Funktionalität der Schaltfläche neu erstellt. Außerdem würde das neue Benutzersteuerelement das abgerundete visuelle Element bereitstellen.

Sie können vermeiden, neue Steuerelemente zu erstellen, indem Sie das visuelle Layout eines vorhandenen Steuerelements anpassen. Mit einer abgerundeten Schaltfläche erstellen Sie eine ControlTemplate mit dem gewünschten visuellen Layout.

Wenn Sie jedoch ein Steuerelement mit neuen Funktionen, anderen Eigenschaften und neuen Einstellungen benötigen, erstellen Sie ein neues UserControl-Element.

Voraussetzungen

Erstellen Sie eine neue WPF-Anwendung, und legen Sie in MainWindow.xaml (oder in einem anderen Fenster Ihrer Wahl) die folgenden Eigenschaften für das <Window>-Element fest:

Eigenschaft Wert
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Legen Sie den Inhalt der <Window>-Elements auf den folgenden XAML-Code fest:

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

Schließlich sollte die Datei MainWindow.xaml ähnlich wie im Folgenden aussehen:

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

Wenn Sie die Anwendung ausführen, sieht dies wie folgt aus:

WPF window with two unstyled buttons

Erstellen einer ControlTemplate

Eine ControlTemplate wird üblicherweise deklariert, indem sie im Abschnitt Resources einer XAML-Datei als Ressource deklariert wird. Da es sich bei Vorlagen um Ressourcen handelt, unterliegen sie denselben Gültigkeitsbereichsregeln, die für alle Ressourcen gelten. Einfach ausgedrückt: Wo Sie eine Vorlage deklarieren, wirkt sich darauf aus, wo die Vorlage angewendet werden kann. Wenn Sie die Vorlage beispielsweise im Stammelement der XAML-Anwendungsdefinitionsdatei deklarieren, kann die Vorlage überall in Ihrer Anwendung verwendet werden. Wenn Sie die Vorlage in einem Fenster definieren, können nur die Steuerelemente in diesem Fenster die Vorlage verwenden.

Fügen Sie zunächst der Datei MainWindow.xaml ein Window.Resources-Element hinzu:

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

Erstellen Sie eine neue <ControlTemplate> mit den folgenden Eigenschaften:

Eigenschaft Wert
x:Key roundbutton
TargetType Button

Diese Steuerelementvorlage ist einfach:

  • ein Stammelement für das-Steuerelement, ein Grid
  • ein Ellipse-Element, um das abgerundete Aussehen der Schaltfläche zu zeichnen
  • ein ContentPresenter-Element, um den benutzerdefinierten Schaltflächeninhalt anzuzeigen
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBinding

Beim Erstellen einer neuen ControlTemplate können Sie weiterhin die öffentlichen Eigenschaften verwenden, um die Darstellung der Steuerelemente anzupassen. Die TemplateBinding-Markuperweiterung bindet eine Eigenschaft eines in der ControlTemplate enthaltenen Elements an eine vom Steuerelement definierte öffentliche Eigenschaft. Bei Verwendung einer TemplateBinding können Steuerelementeigenschaften als Parameter der Vorlage fungieren. D.h., beim Festlegen einer Steuerelementeigenschaft wird dieser Wert an das Element mit TemplateBinding übergeben.

Ellipse

Beachten Sie, dass die Eigenschaften Fill und Stroke des Elements <Ellipse> an die Eigenschaften Foreground und Background des Steuerelements gebunden sind.

ContentPresenter

Der Vorlage wird auch ein <ContentPresenter>-Element hinzugefügt. Da diese Vorlage für eine Schaltfläche konzipiert ist, müssen Sie berücksichtigen, dass die Schaltfläche von ContentControl erbt. Die Schaltfläche stellt den Inhalt des Elements dar. Sie können beliebige Elemente innerhalb der Schaltfläche festlegen, z. B. Nur-Text oder sogar ein anderes Steuerelement. Beide folgenden Schaltflächen sind gültig:

<Button>My Text</Button>

<!-- and -->

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

In den beiden vorherigen Beispielen werden der Text und das Kontrollkästchen als Button.Content-Eigenschaft festgelegt. Was auch immer als Inhalt festgelegt wird, kann durch einen <ContentPresenter> dargestellt werden, was die Vorlage auch leistet.

Wenn die ControlTemplate auf einen ContentControl-Typ angewendet wird, z. B. auf ein Button-Element, wird nach einem ContentPresenter-Element in der-Elementstruktur gesucht. Wenn der ContentPresenter gefunden wird, bindet die Vorlage automatisch die Content-Eigenschaft des Steuerelements an den ContentPresenter.

Verwenden der Vorlage

Suchen Sie die Schaltflächen, die zu Beginn dieses Artikels deklariert wurden.

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

Legen Sie die Template-Eigenschaft der zweiten Schaltfläche auf die roundbutton-Ressource fest:

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

Wenn Sie das Projekt ausführen und das Ergebnis untersuchen, sehen Sie, dass die Schaltfläche einen abgerundeten Hintergrund aufweist.

WPF window with one template oval button

Vielleicht haben Sie bemerkt, dass die Schaltfläche kein Kreis ist, sondern verzerrt ist. Aufgrund der Art und Weise, wie das <Ellipse>-Element funktioniert, wird sie immer erweitert, um den verfügbaren Platz auszufüllen. Vereinheitlichen Sie den Kreis, indem Sie die Eigenschaften der Schaltfläche width und height in den gleichen Wert ändern:

<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

Hinzufügen eines Triggers

Obwohl eine Schaltfläche mit einer angewendeten Vorlage anders aussieht, verhält sie sich wie jede andere Schaltfläche. Wenn Sie auf die Schaltfläche klicken, wird das Click-Ereignis ausgelöst. Möglicherweise haben Sie jedoch bemerkt, dass die visuellen Elemente der Schaltfläche nicht geändert werden, wenn Sie den Mauszeiger über die Schaltfläche bewegen. Diese visuellen Interaktionen werden alle durch die Vorlage definiert.

Durch die dynamischen Ereignis- und Eigenschaftensysteme, die von WPF bereitgestellt werden, können Sie eine bestimmte Eigenschaft für einen Wert beobachten und die Vorlage ggf. neu formatieren. In diesem Beispiel beobachten Sie die IsMouseOver-Eigenschaft der Schaltfläche. Wenn sich der Mauszeiger über dem Steuerelement befindet, formatieren Sie die <Ellipse> mit einer neuen Farbe. Dieser Triggertyp wird als PropertyTrigger bezeichnet.

Damit dies funktioniert, müssen Sie <Ellipse> einen Namen hinzufügen, auf den Sie verweisen können. Vergeben Sie den Namen backgroundElement.

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

Fügen Sie als nun der ControlTemplate.Triggers-Sammlung einen neuen Trigger hinzu. Der Trigger überwacht das IsMouseOver-Ereignis für den Wert 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>

Fügen Sie dem <Trigger> nun einen <Setter> hinzu, der die Fill-Eigenschaft von <Ellipse> in eine neue Farbe ändert.

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

Führen Sie das Projekt aus. Beachten Sie, dass beim Bewegen der Maus über die Schaltfläche sich die Farbe der <Ellipse> ändert.

mouse moves over WPF button to change the fill color

Verwenden eines VisualState

Visuelle Zustände werden von einem-Steuerelement definiert und ausgelöst. Wenn die Maus z. B. über das Steuerelement bewegt wird, wird der CommonStates.MouseOver-Zustand ausgelöst. Sie können Eigenschaftsänderungen auf der Grundlage des aktuellen Zustands des Steuerelements animieren. Im vorherigen Abschnitt wurde ein <PropertyTrigger> verwendet, um den Hintergrund der Schaltfläche in AliceBlue zu ändern, wenn die IsMouseOver-Eigenschaft true war. Erstellen Sie stattdessen einen visuellen Zustand, der die Änderung dieser Farbe animiert und für einen fließenden Übergang sorgt. Weitere Informationen zu VisualStates finden Sie unter Stile und Vorlagen in WPF.

Wenn Sie den <PropertyTrigger> in einen animierten visuellen Zustand konvertieren möchten, entfernen Sie zunächst das <ControlTemplate.Triggers>-Element aus der Vorlage.

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

Fügen Sie dann im <Grid>-Stamm der Steuerelementvorlage das <VisualStateManager.VisualStateGroups>-Element mit einer <VisualStateGroup> für CommonStates hinzu. Definieren Sie zwei Zustände, Normal und MouseOver.

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

Alle in einem <VisualState> definierten Animationen werden angewendet, wenn dieser Zustand ausgelöst wird. Erstellen Sie Animationen für jeden Zustand. Animationen werden in einem <Storyboard>-Element abgelegt. Weitere Informationen zu Storyboards finden Sie unter Übersicht über Storyboards.

  • Normal

    Dieser Zustand animiert die Ellipsenfüllung und stellt sie in der Farbe Background des Steuerelements wieder her.

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

    Dieser Zustand animiert die Farbe Background der Ellipse in eine neue Farbe: Yellow.

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

Die <ControlTemplate> sollte nun wie folgt aussehen.

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

Führen Sie das Projekt aus. Beachten Sie, dass beim Bewegen der Maus über die Schaltfläche sich die Farbe der <Ellipse> animiert wird.

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

Nächste Schritte