Läs på engelska

Dela via


Så här skapar du en mall för en kontroll (WPF.NET)

Med Windows Presentation Foundation (WPF) kan du anpassa en befintlig kontrolls visuella struktur och beteende med din egen återanvändbara mall. Mallar kan tillämpas globalt på ditt program, fönster och sidor eller direkt på kontroller. De flesta scenarier som kräver att du skapar en ny kontroll kan täckas genom att i stället skapa en ny mall för en befintlig kontroll.

I den här artikeln utforskar du att skapa en ny ControlTemplate för Button-kontrollen.

När du ska skapa ett ControlTemplate

Kontroller har många egenskaper, till exempel Background, Foregroundoch FontFamily. De här egenskaperna styr olika aspekter av kontrollens utseende, men de ändringar som du kan göra genom att ange dessa egenskaper är begränsade. Du kan till exempel ställa in egenskapen Foreground till blå och FontStyle till kursiv stil på egenskapen CheckBox. När du vill anpassa kontrollens utseende utöver vilken inställning de andra egenskaperna på kontrollen kan göra skapar du en ControlTemplate.

I de flesta användargränssnitt har en knapp samma allmänna utseende: en rektangel med viss text. Om du vill skapa en avrundad knapp kan du skapa en ny kontroll som ärver från knappen eller återskapar funktionen för knappen. Dessutom skulle den nya användarkontrollen ge det cirkulära visuella.

Du kan undvika att skapa nya kontroller genom att anpassa den visuella layouten för en befintlig kontroll. Med en avrundad knapp skapar du en ControlTemplate med önskad visuell layout.

Om du däremot behöver en kontroll med nya funktioner, olika egenskaper och nya inställningar skapar du en ny UserControl.

Förutsättningar

Skapa ett nytt WPF-program och i MainWindow.xaml- (eller något annat fönster) anger du följande egenskaper i elementet <Window>:

Egenskap Värde
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

Ange innehållet i elementet <Window> till följande XAML:

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

I slutändan bör filen MainWindow.xaml se ut ungefär så här:

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

Om du kör programmet ser det ut så här:

WPF-fönster med två oformaterade knappar

Skapa ett ControlTemplate

Det vanligaste sättet att deklarera en ControlTemplate är som en resurs i avsnittet Resources i en XAML-fil. Eftersom mallar är resurser följer de samma omfångsregler som gäller för alla resurser. Enkelt uttryckt, där du deklarerar en mall påverkar var mallen kan tillämpas. Om du till exempel deklarerar mallen i rotelementet i din XAML-fil för programdefinitionen kan mallen användas var som helst i programmet. Om du definierar mallen i ett fönster kan endast kontrollerna i det fönstret använda mallen.

Börja med att lägga till ett Window.Resources-element i MainWindow.xaml--fil:

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>

Skapa en ny <ControlTemplate-> med följande egenskapsuppsättning:

Egenskap Värde
x:Key roundbutton
TargetType Button

Den här kontrollmallen är enkel:

  • ett rotelement för kontrollen, en Grid
  • en Ellipse för att rita knappens rundade utseende
  • en ContentPresenter för att visa det användardefinierade knappinnehållet
XAML
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBinding

När du skapar en ny ControlTemplatekanske du fortfarande vill använda de publika egenskaperna för att ändra utseendet på kontrollen. TemplateBinding markeringstillägg binder en egenskap för ett element som finns i ControlTemplate till en offentlig egenskap som definieras av kontrollen. När du använder en TemplateBindingaktiverar du egenskaper för kontrollen för att fungera som parametrar för mallen. När en egenskap på en kontroll har angetts skickas det värdet till elementet som har TemplateBinding- på den.

Ellips

Observera att egenskaperna Fill och Stroke för elementet <Ellipse> är bundna till kontrollens egenskaper för Foreground och Background.

ContentPresenter

Ett <ContentPresenter->-element läggs också till i mallen. Eftersom den här mallen är utformad för en knapp bör du tänka på att knappen ärver från ContentControl. Knappen visar elementets innehåll. Du kan ange vad som helst inuti knappen, till exempel oformaterad text eller till och med en annan kontroll. Båda följande är giltiga knappar:

XAML
<Button>My Text</Button>

<!-- and -->

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

I båda föregående exempel anges texten och kryssrutan som egenskapen Button.Content. Vad som än anges som innehåll kan visas via en <ContentPresenter>, vilket är vad mallen gör.

Om ControlTemplate tillämpas på en ContentControl typ, till exempel en Button, söks en ContentPresenter efter i elementträdet. Om ContentPresenter hittas binder mallen automatiskt kontrollens Content-egenskap till ContentPresenter.

Använda mallen

Hitta knapparna som deklarerades i början av den här artikeln.

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

Ange den andra knappens egenskap Template till den roundbutton resursen:

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

Om du kör projektet och tittar på resultatet ser du att knappen har en avrundad bakgrund.

WPF-fönster med en mallbaserad oval knapp

Du kanske har märkt att knappen inte är en cirkel men är skev. På grund av hur elementet <Ellipse> fungerar expanderar det alltid för att fylla det tillgängliga utrymmet. Gör cirkeln enhetlig genom att ändra knappens width och height egenskaper till samma värde:

XAML
<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-fönster med en cirkelknapp för mallar

Lägga till en utlösare

Även om en knapp med en mall som används ser annorlunda ut fungerar den på samma sätt som andra knappar. Om du trycker på knappen utlöses händelsen Click. Men du kanske har märkt att när du flyttar musen över knappen ändras inte knappens visuella objekt. Alla dessa visuella interaktioner definieras av mallen.

Med de dynamiska händelse- och egenskapssystem som WPF tillhandahåller kan du titta på en specifik egenskap för ett värde och sedan ändra stil på mallen när det är lämpligt. I det här exemplet tittar du på knappens egenskap IsMouseOver. När musen är över kontrollen, formatera <Ellipse> med en ny färg. Den här typen av utlösare kallas PropertyTrigger.

För att det ska fungera måste du lägga till ett namn i <Ellipse-> som du kan referera till. Ge den namnet på backgroundElement.

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

Lägg sedan till en ny Trigger i samlingen ControlTemplate.Triggers. Utlösaren övervakar händelsen IsMouseOver för värdet true.

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

Lägg sedan till en <Setter> till <Trigger> som ändrar egenskapen Fill för <Ellipse> till en ny färg.

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

Kör projektet. Observera att när du flyttar musen över knappen ändras färgen på <Ellipse>.

mus flyttas över WPF-knappen för att ändra fyllningsfärgen

Använd en VisualState

Visuella tillstånd definieras och utlöses av en kontroll. När musen till exempel flyttas ovanpå kontrollen utlöses CommonStates.MouseOver-tillståndet. Du kan animera egenskapsändringar baserat på kontrollens aktuella tillstånd. I föregående avsnitt användes <PropertyTrigger> för att ändra knappens bakgrund till AliceBlue när egenskapen IsMouseOver var true. Skapa i stället ett visuellt tillstånd som animerar ändringen av den här färgen, vilket ger en smidig övergång. Mer information om VisualStatesfinns i Formatmallar och mallar i WPF.

Om du vill konvertera <PropertyTrigger> till ett animerat visuellt tillstånd tar du först bort elementet <ControlTemplate.Triggers> från mallen.

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

Lägg sedan till elementet <VisualStateManager.VisualStateGroups> i <Grid>, kontrollmallens rot, med ett <VisualStateGroup> för CommonStates. Definiera två tillstånd, Normal och MouseOver.

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

Alla animeringar som definieras i en <VisualState-> tillämpas när detta tillstånd utlöses. Skapa animeringar för varje tillstånd. Animeringar placeras inuti ett <Storyboard-> element. Mer information om storyboards finns i Storyboards Overview.

  • Normal

    Det här tillståndet animerar ellipsfyllningen och återställer den till kontrollens Background färg.

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

    Det här tillståndet animerar ellipsen Background färg till en ny färg: Yellow.

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

<ControlTemplate-> bör nu se ut så här.

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

Kör projektet. Observera att när du flyttar musen över knappen animerar färgen på <Ellipse>.

mus flyttas över WPF-knappen för att ändra fyllningsfärgen med ett visuellt tillstånd

Nästa steg