建立控制項的範本Create a template for a control

使用 Windows Presentation Foundation (WPF) ,您可以使用自己可重複使用的範本自訂現有控制項的視覺化結構和行為。With Windows Presentation Foundation (WPF), you can customize an existing control's visual structure and behavior with your own reusable template. 範本可全域套用至您的應用程式、windows 和頁面,或直接套用至控制項。Templates can be applied globally to your application, windows and pages, or directly to controls. 您可以改為建立現有控制項的新範本,以涵蓋需要您建立新控制項的大部分案例。Most scenarios that require you to create a new control can be covered by instead creating a new template for an existing control.

重要

.NET 5 (和 .NET Core) 的桌面指南檔正在結構中。The Desktop Guide documentation for .NET 5 (and .NET Core) is under construction.

在本文中,您將探索如何為控制項建立新的 ControlTemplate ButtonIn this article, you'll explore creating a new ControlTemplate for the Button control.

建立 ControlTemplate 的時機When to create a ControlTemplate

控制項有許多屬性,例如 BackgroundForegroundFontFamilyControls have many properties, such as Background, Foreground, and FontFamily. 這些屬性會控制控制面板的不同層面,但您可以藉由設定這些屬性進行的變更會受到限制。These properties control different aspects of the control's appearance, but the changes that you can make by setting these properties are limited. 例如,您可以將屬性設定 Foreground 為 [藍色],並將設定 FontStyle 為 [斜體] CheckBoxFor example, you can set the Foreground property to blue and FontStyle to italic on a CheckBox. 如果您想要自訂控制項的外觀,而不是控制項上其他屬性所能做的設定,您可以建立 ControlTemplateWhen you want to customize the control's appearance beyond what setting the other properties on the control can do, you create a ControlTemplate.

在大部分的使用者介面中,按鈕具有相同的一般外觀:有一些文字的矩形。In most user interfaces, a button has the same general appearance: a rectangle with some text. 如果您想要建立圓角按鈕,您可以建立繼承自按鈕的新控制項,或重新建立按鈕的功能。If you wanted to create a rounded button, you could create a new control that inherits from the button or recreates the functionality of the button. 此外,新的使用者控制項會提供圓形視覺效果。In addition, the new user control would provide the circular visual.

您可以自訂現有控制項的視覺化配置,以避免建立新的控制項。You can avoid creating new controls by customizing the visual layout of an existing control. 使用圓角按鈕時,您可以 ControlTemplate 使用所需的視覺版面配置來建立。With a rounded button, you create a ControlTemplate with the desired visual layout.

另一方面,如果您需要的控制項有新功能、不同的屬性和新的設定,您可以建立新的 UserControlOn the other hand, if you need a control with new functionality, different properties, and new settings, you would create a new UserControl.

必要條件Prerequisites

建立新的 WPF 應用程式,並在 MainWindow 中 (或您選擇的另一個視窗) 在 : :: no loc () ::: 元素上設定下列屬性:Create a new WPF application and in MainWindow.xaml (or another window of your choice) set the following properties on the <Window> element:

Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

: ::非 loc () ::: 元素的內容設定為下列 XAML:Set the content of the <Window> element to the following XAML:

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

最後, MainWindow .xaml 檔案看起來應該會如下所示:In the end, the MainWindow.xaml file should look similar to the following:

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

如果您執行應用程式,它看起來如下所示:If you run the application, it looks like the following:

具有兩個 .unstyled-list 按鈕的 WPF 視窗

建立 ControlTemplateCreate a ControlTemplate

最常見的宣告方式是在 ControlTemplate XAML 檔案中的區段中做為資源 ResourcesThe most common way to declare a ControlTemplate is as a resource in the Resources section in a XAML file. 因為範本是資源,所以它們會遵守適用于所有資源的相同範圍規則。Because templates are resources, they obey the same scoping rules that apply to all resources. 單純地說,您宣告範本的位置會影響可以套用範本的位置。Put simply, where you declare a template affects where the template can be applied. 例如,如果您在應用程式定義 XAML 檔案的根項目中宣告範本,則範本可以在應用程式中的任何位置使用。For example, if you declare the template in the root element of your application definition XAML file, the template can be used anywhere in your application. 如果您在視窗中定義範本,則只有該視窗中的控制項可以使用該範本。If you define the template in a window, only the controls in that window can use the template.

若要開始,請將專案新增 Window.Resources 至您的 MainWindow .xaml 檔案:To start with, add a Window.Resources element to your MainWindow.xaml file:

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

建立新的 : ::非 loc () ::: 並設定下列屬性:Create a new <ControlTemplate> with the following properties set:

x:Keyx:Key roundbutton
TargetType Button

此控制項範本將很簡單:This control template will be simple:

  • 控制項的根項目,a Grida root element for the control, a Grid
  • Ellipse,用來繪製按鈕的圓角外觀an Ellipse to draw the rounded appearance of the button
  • ContentPresenter,顯示使用者指定的按鈕內容a ContentPresenter to display the user-specified button content
<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</ControlTemplate>

TemplateBindingTemplateBinding

當您建立新的時 ControlTemplate ,您仍然可能會想要使用公用屬性來變更控制項的外觀。When you create a new ControlTemplate, you still might want to use the public properties to change the control's appearance. TemplateBinding標記延伸會將中的元素屬性系結 ControlTemplate 至控制項所定義的公用屬性。The TemplateBinding markup extension binds a property of an element that is in the ControlTemplate to a public property that is defined by the control. 當您使用 TemplateBinding時,您可以讓控制項上的屬性作為範本的參數。When you use a TemplateBinding, you enable properties on the control to act as parameters to the template. 也就是說,已設定控制項上的屬性時,該值會傳遞給具有 TemplateBinding 的元素。That is, when a property on a control is set, that value is passed on to the element that has the TemplateBinding on it.

橢圓形Ellipse

請注意 Fill :: Stroke : no loc () ::: element 的和屬性會系結至控制項的 ForegroundBackground 屬性。Notice that the Fill and Stroke properties of the <Ellipse> element are bound to the control's Foreground and Background properties.

ContentPresenterContentPresenter

您也會將 : ::非 loc () :::專案新增至範本。A <ContentPresenter> element is also added to the template. 因為這個範本是針對按鈕所設計,請考慮按鈕繼承自 ContentControlBecause this template is designed for a button, take into consideration that the button inherits from ContentControl. 按鈕會顯示元素的內容。The button presents the content of the element. 您可以設定按鈕內的任何內容,例如純文字或甚至另一個控制項。You can set anything inside of the button, such as plain text or even another control. 下列兩項都是有效的按鈕:Both of the following are valid buttons:

<Button>My Text</Button>

<!-- and -->

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

在上述兩個範例中,文字和核取方塊都會設定為 [ 內容 ] 屬性。In both of the previous examples, the text and the checkbox are set as the Button.Content property. 無論內容為何,都可以透過 : ::非 loc () ::: 來呈現,這就是範本的用途。Whatever is set as the content can be presented through a <ContentPresenter>, which is what the template does.

如果套用 ControlTemplateContentControl 類型(例如 Button ),則 ContentPresenter 會在元素樹狀結構中搜尋。If the ControlTemplate is applied to a ContentControl type, such as a Button, a ContentPresenter is searched for in the element tree. 如果 ContentPresenter 找到,則範本會自動將控制項的屬性系結 ContentContentPresenterIf the ContentPresenter is found, the template automatically binds the control's Content property to the ContentPresenter.

使用範本Use the template

尋找在本文一開始所宣告的按鈕。Find the buttons that were declared at the start of this article.

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

將第二個按鈕的 Template 屬性設為 roundbutton 資源:Set the second button's Template property to the roundbutton resource:

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

如果您執行專案並查看結果,您會看到按鈕具有圓角背景。If you run the project and look at the result, you'll see that the button has a rounded background.

具有一個範本 oval 按鈕的 WPF 視窗

您可能已經注意到按鈕不是圓形,但卻扭曲。You may have noticed that the button isn't a circle but is skewed. 由於 : :: no loc () ::: element 的運作方式,它一律會展開以填滿可用的空間。Because of the way the <Ellipse> element works, it always expands to fill the available space. 將按鈕的 widthheight 屬性變更為相同的值,讓圓形保持一致:Make the circle uniform by changing the button's width and height properties to the same value:

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

新增觸發程式Add a Trigger

雖然套用範本的按鈕看起來不同,但它的行為與任何其他按鈕的行為相同。Even though a button with a template applied looks different, it behaves the same as any other button. 如果您按下按鈕,就會 Click 引發事件。If you press the button, the Click event fires. 不過,您可能已經注意到,當您將滑鼠移至按鈕上方時,按鈕的視覺效果不會變更。However, you may have noticed that when you move your mouse over the button, the button's visuals don't change. 這些視覺效果互動全都由範本所定義。These visual interactions are all defined by the template.

使用 WPF 提供的動態事件和屬性系統,您可以監看某個值的特定屬性,然後在適當的情況下重新造型範本。With the dynamic event and property systems that WPF provides, you can watch a specific property for a value and then restyle the template when appropriate. 在此範例中,您將監看按鈕的 IsMouseOver 屬性。In this example, you'll watch the button's IsMouseOver property. 當滑鼠停留在控制項上時,使用新的色彩來將 : :: no loc () ::: 樣式。When the mouse is over the control, style the <Ellipse> with a new color. 這種類型的觸發程式稱為 PropertyTriggerThis type of trigger is known as a PropertyTrigger.

若要這樣做,您必須將名稱新增至您可以參考的 : :: no loc () :::For this to work, you'll need to add a name to the <Ellipse> that you can reference. 指定 backgroundElement 的名稱。Give it the name of backgroundElement.

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

接下來,將新的新增 TriggerControlTemplate 集合。Next, add a new Trigger to the ControlTemplate.Triggers collection. 觸發程式會監看 IsMouseOver 值的事件 trueThe trigger will watch the IsMouseOver event for the value 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>

接下來,將 : ::非 loc () : ::新增至 : ::非 loc () ::: ,將 : :: no loc () :::Fill 屬性變更為新的色彩。Next, add a <Setter> to the <Trigger> that changes the Fill property of the <Ellipse> to a new color.

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

執行專案。Run the project. 請注意,當您將滑鼠移至按鈕上方時,:: : no loc () ::: 會變更色彩。Notice that when you move the mouse over the button, the color of the <Ellipse> changes.

滑鼠移至 WPF 按鈕以變更填滿色彩

使用 VisualStateUse a VisualState

視覺狀態是由控制項所定義和觸發。Visual states are defined and triggered by a control. 例如,當滑鼠移到控制項上方時, CommonStates.MouseOver 就會觸發狀態。For example, when the mouse is moved on top of the control, the CommonStates.MouseOver state is triggered. 您可以根據控制項目前的狀態,建立屬性變更的動畫。You can animate property changes based on the current state of the control. 在上一節中,已使用 : :: no loc () ::: 將按鈕的前景變更為 AliceBlue 當屬性為時 IsMouseOver trueIn the previous section, a <PropertyTrigger> was used to change the foreground of the button to AliceBlue when the IsMouseOver property was true. 相反地,請建立以動畫顯示此色彩變更的視覺狀態,以提供順暢的轉換。Instead, create a visual state that animates the change of this color, providing a smooth transition. 如需 VisualStates 的詳細資訊,請參閱 WPF 中的樣式和範本For more information about VisualStates, see Styles and templates in WPF.

若要將 : :: no loc () ::: 轉換為動畫的視覺狀態,請先 <ControlTemplate.Triggers> 從您的範本中移除該元素。To convert the <PropertyTrigger> to an animated visual state, First, remove the <ControlTemplate.Triggers> element from your template.

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

接下來,在控制項範本的 : :: no loc () : : root 中,加入 : :: No-Loc ( # B0 VisualStateManager. system.windows.visualstatemanager.visualstategroups>) ::: element,其具有 : ::無 loc () ::: CommonStatesNext, in the <Grid> root of the control template, add the <VisualStateManager.VisualStateGroups> element with a <VisualStateGroup> for CommonStates. 定義兩個狀態: NormalMouseOverDefine two states, Normal and 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>

當觸發該狀態時,會套用在 : ::非 loc () ::: 中定義的任何動畫。Any animations defined in a <VisualState> are applied when that state is triggered. 建立每個狀態的動畫。Create animations for each state. 動畫會置於 : ::非 loc () ::: 元素內。Animations are put inside of a <Storyboard> element. 如需分鏡腳本的詳細資訊,請參閱分鏡腳本 總覽For more information about storyboards, see Storyboards Overview.

  • 正常Normal

    此狀態會將橢圓形填滿,並將其還原為控制項的 Background 色彩。This state animates the ellipse fill, restoring it to the control's Background color.

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

    這個狀態會將橢圓形 Background 色彩繪製成新的色彩: YellowThis state animates the ellipse Background color to a new color: Yellow.

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

: ::非 loc () ::: 現在看起來應該如下所示。The <ControlTemplate> should now look like the following.

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

執行專案。Run the project. 請注意,當您將滑鼠移至按鈕上方時,:: : no loc () ::: 動畫的色彩。Notice that when you move the mouse over the button, the color of the <Ellipse> animates.

滑鼠移至 WPF 按鈕以變更填滿色彩

後續步驟Next steps