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

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

重要

.NET 7 和 .NET 6 的桌面指南檔正在進行建構。

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

建立 ControlTemplate 的時機

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

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

您可以自訂現有控制項的視覺配置,以避免建立新的控制項。 使用圓角按鈕,您可以使用所需的視覺效果配置來建立 ControlTemplate

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

必要條件

建立新的 WPF 應用程式,並在MainWindow.xaml (或您選擇的另一個視窗) 在 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 視窗

建立 ControlTemplate

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

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

橢圓形

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

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

您可能已注意到按鈕不是圓形,但扭曲。 由於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 視窗

新增觸發程式

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

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

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

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

接下來,將新的 Trigger 新增至 ControlTemplate.Triggers 集合。 觸發程式會watch 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>

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

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

使用 VisualState

視覺狀態是由 控制項所定義和觸發。 例如,當滑鼠移至控制項頂端時,就會 CommonStates.MouseOver 觸發狀態。 您可以根據控制項的目前狀態,建立屬性變更的動畫效果。 在上一< 節中,PropertyTrigger >是用來將屬性為 時 IsMouseOver ,將按鈕的背景變更為 AliceBluetrue 。 相反地,請建立視覺化狀態,以動畫顯示此色彩的變更,以提供順暢的轉換。 如需 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 >根目錄中,使用< 的 VisualStateGroup 新增 VisualStateManager.VisualStateGroups >> 元素。 <CommonStates 定義兩個狀態和 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>

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

滑鼠移至 WPF 按鈕上方,以變更具有視覺狀態的填滿色彩

下一步