如何建立控制項的範本 (WPF.NET)

使用 Windows Presentation Foundation (WPF),您可以使用您自己的可重複使用範本來自訂現有控制項的視覺結構和行為。 範本可以全域套用至您的應用程式、視窗和頁面,或直接套用至控制項。 大部分需要您建立新控制項的案例,都可以改為建立現有控制項的新範本來涵蓋。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

在本文中,您將探索為 Button 控制項建立新的 ControlTemplate

建立 ControlTemplate 的時機

控制項有許多屬性,例如 BackgroundForegroundFontFamily 。 這些屬性會控制控制面板的不同層面,但是您可以藉由設定這些屬性所做的變更會受到限制。 例如,您可以將 屬性設定為藍色,並在 FontStyleCheckBox 設定 Foreground 為斜體。 當您想要自訂控制項的外觀超出控制項上設定其他屬性可以執行的動作時,您可以建立 ControlTemplate

在大部分的使用者介面中,按鈕具有相同的一般外觀:具有某些文字的矩形。 如果您想要建立四捨五入的按鈕,您可以建立繼承自按鈕的新控制項,或重新建立按鈕的功能。 此外,新的使用者控制項會提供圓形視覺效果。

您可以自訂現有控制項的視覺配置,以避免建立新的控制項。 使用四捨五入的按鈕,您可以使用所需的視覺配置來建立 ControlTemplate

另一方面,如果您需要具有新功能、不同屬性和新設定的控制項,您會建立新的 UserControl

必要條件

在 MainWindow.xaml 中建立新的 WPF 應用程式 (或您選擇的另一個視窗)在 < Window > 元素上設定下列屬性:

屬性
Title Template Intro Sample
SizeToContent WidthAndHeight
MinWidth 250

將 Window > 元素的內容 < 設定為下列 XAML:

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

最後, MainWindow.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>

如果您執行應用程式,看起來如下所示:

WPF window with two unstyled buttons

建立 ControlTemplate

宣告 ControlTemplate 的最常見方式是作為 XAML 檔案區 Resources 段中的資源。 因為範本是資源,所以會遵守套用至所有資源的相同範圍規則。 簡單地說,您宣告範本會影響可套用範本的位置。 例如,如果您在應用程式定義 XAML 檔案的根項目中宣告範本,則可以在應用程式中的任何位置使用範本。 如果您在視窗中定義範本,則只有該視窗中的控制項可以使用範本。

若要從 開始,請將 Window.Resources 元素新增至 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>

使用下列屬性集建立新的 ControlTemplate > : <

屬性
x:Key roundbutton
TargetType Button

此控制項範本很簡單:

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

TemplateBinding

當您建立新的 ControlTemplate 時,您仍可能想要使用公用屬性來變更控制項的外觀。 TemplateBinding 標記延伸會將 中的 ControlTemplate 專案屬性系結至 控制項所定義的公用屬性。 當您使用 TemplateBinding 時,您可以在控制項上啟用屬性做為範本的參數。 也就是說,已設定控制項上的屬性時,該值會傳遞給具有 TemplateBinding 的元素。

橢圓形

請注意, Fill Ellipse > 專案的 和 Stroke 屬性 < 會系結至控制項的 ForegroundBackground 屬性。

ContentPresenter

< ContentPresenter > 元素也會新增至範本。 由於此範本是針對按鈕所設計,因此請考慮到按鈕繼承自 ContentControl 。 按鈕會顯示專案的內容。 您可以在按鈕內設定任何專案,例如純文字或甚至是另一個控制項。 下列兩者都是有效的按鈕:

<Button>My Text</Button>

<!-- and -->

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

在上述兩個範例中,文字和核取方塊都會設定為 Button.Content 屬性。 任何設定為內容都可以透過 < ContentPresenter > 呈現的內容,這是範本的功能。

ControlTemplate如果 套用至 ContentControl 型別,例如 ButtonContentPresenter 則會在專案樹狀結構中搜尋 。 ContentPresenter如果找到 ,範本會自動將控制項的 Content 屬性系結至 ContentPresenter

使用範本

尋找本文開頭所宣告的按鈕。

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

將第二個按鈕的 Template 屬性設定為 roundbutton 資源:

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

如果您執行專案並查看結果,您會看到按鈕具有四捨五入的背景。

WPF window with one template oval button

您可能已經注意到按鈕不是圓形,但扭曲。 由於 Ellipse > 元素的運作方式 < ,它一律會展開以填滿可用空間。 將按鈕的 widthheight 屬性變更為相同的值,使圓形統一:

<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

新增觸發程式

即使套用範本的按鈕看起來不同,但它的行為與任何其他按鈕相同。 如果您按下按鈕,就會 Click 引發事件。 不過,您可能已經注意到,當您將滑鼠移至按鈕上方時,按鈕的視覺效果不會變更。 這些視覺互動都是由範本所定義。

使用 WPF 提供的動態事件和屬性系統,您可以監看值的特定屬性,然後在適當時重設範本。 在此範例中,您將監看按鈕的 IsMouseOver 屬性。 當滑鼠移至控制項上方時,請使用新的色彩來 < 設定 Ellipse > 的樣式。 這種類型的觸發程式稱為 PropertyTrigger

若要讓這項作業能夠運作,您必須將名稱新增至您可以參考的 Ellipse > 。 < 為它命名 backgroundElement

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

接下來,將新的 Trigger 新增至 ControlTemplate.Triggers 集合。 觸發程式會監看 IsMouseOvertrue 的事件。

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

接下來,將 < Setter 新增至 < 觸發 > 程式 > ,將 Ellipse > 的 < Fill 屬性變更 為新的色彩。

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

執行專案。 請注意,當您將滑鼠移至按鈕上方時,橢圓 > 形的 < 色彩會變更。

mouse moves over WPF button to change the fill color

使用 VisualState

視覺狀態是由 控制項所定義和觸發。 例如,當滑鼠移至控制項頂端時,就會 CommonStates.MouseOver 觸發狀態。 您可以根據控制項的目前狀態,建立屬性變更的動畫效果。 在上一 < 節中,PropertyTrigger > 用來將按鈕 AliceBlue 的背景變更為 當屬性為 trueIsMouseOver 。 相反地,請建立視覺化狀態,以動畫顯示此色彩的變更,以提供平滑的轉換。 如需 VisualStates 的詳細資訊 ,請參閱 WPF 中的樣式和範本。

若要將 PropertyTrigger > 轉換成動畫視覺狀態,首先,請從範本中移除 < ControlTemplate.Triggers > 元素。 <

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

接下來,在 控制項範本的 Grid > 根目錄中,使用 <<CommonStates VisualStateGroup 新增 VisualStateManager.VisualStateGroups >> 元素。 < 定義兩個狀態和 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>

觸發該狀態時,會套用 VisualState >< 定義的任何動畫。 為每個狀態建立動畫。 動畫會放在 Storyboard 元素內 < 。 > 如需腳本的詳細資訊,請參閱 分鏡腳本概觀

  • 正常

    此狀態會以動畫顯示省略號填滿,並將它還原至控制項的 Background 色彩。

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

    此狀態會將省略 Background 號色彩動畫化為新的色彩: Yellow

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

< ControlTemplate > 現在看起來應該如下所示。

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

執行專案。 請注意,當您將滑鼠移至按鈕上方時,橢圓形 >< 色彩會以動畫顯示。

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

下一步