樣式和範本 (WPF .NET)
Windows Presentation Foundation (WPF) 樣式和範本化是指一套功能,可讓開發人員和設計工具為其產品建立視覺上吸引人的效果和一致的外觀。 自訂應用程式的外觀時,您想要強式樣式和範本化模型,以在應用程式內和之間維護及共用外觀。 WPF 會提供該模型。
WPF 樣式模型的另一個功能是簡報和邏輯的分離。 設計工具只能使用 XAML 來處理應用程式的外觀,同時開發人員可以使用 C# 或 Visual Basic 來處理常式設計邏輯。
本概觀著重于應用程式的樣式和範本化層面,且不會討論任何資料系結概念。 如需有關資料繫結的資訊,請參閱資料繫結概觀。
請務必瞭解資源,這是可重複使用樣式和範本的功能。 如需資源的詳細資訊,請參閱 XAML 資源 概觀。
重要
.NET 7 和 .NET 6 的桌面指南檔正在建置中。
範例
本概觀中提供的範例程式碼是以下圖所示的簡單相片流覽應用程式 為基礎 。
這個簡單的相片範例使用樣式設定和範本化,來創造引人注目的使用者體驗。 此範例有兩 TextBlock 個 ListBox 元素和系結至影像清單的控制項。
如需完整範例,請參閱樣式設定和範本化範例簡介 (英文)。
樣式
您可以將 視為 Style 將一組屬性值套用至多個元素的便利方式。 您可以在任何衍生自 FrameworkElement 或 例如 Window 或 FrameworkContentElementButton 的專案上使用樣式。
宣告樣式的最常見方式是作為 XAML 檔案區 Resources
段中的資源。 因為樣式是資源,所以會遵守套用至所有資源的相同範圍規則。 簡單地說,宣告樣式會影響套用樣式的位置。 例如,如果您在應用程式定義 XAML 檔案的根項目中宣告樣式,則可以在應用程式中的任何位置使用樣式。
例如,下列 XAML 程式碼會宣告 的兩個 TextBlock
樣式,一個會自動套用至所有 TextBlock
元素,另一個必須明確參考。
<Window.Resources>
<!-- .... other resources .... -->
<!--A Style that affects all TextBlocks-->
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="FontSize" Value="14"/>
</Style>
<!--A Style that extends the previous TextBlock Style with an x:Key of TitleText-->
<Style BasedOn="{StaticResource {x:Type TextBlock}}"
TargetType="TextBlock"
x:Key="TitleText">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Foreground">
<Setter.Value>
<LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.0" Color="#90DDDD" />
<GradientStop Offset="1.0" Color="#5BFFFF" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
以下是上述所宣告的樣式範例。
<StackPanel>
<TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
<TextBlock>Check out my new pictures!</TextBlock>
</StackPanel>
如需詳細資訊,請參閱 建立控制項 的樣式。
ControlTemplates
在 WPF 中, ControlTemplate 控制項的 會定義控制項的外觀。 您可以藉由定義新的 ControlTemplate 並將它指派給控制項,來變更控制項的結構和外觀。 在許多情況下,範本提供足夠的彈性,讓您不需要撰寫自己的自訂控制項。
每個控制項都有指派給 Control.Template 屬性的預設範本。 範本會將控制項的視覺呈現與控制項的功能連接起來。 由於您在 XAML 中定義範本,因此您可以變更控制項的外觀,而不需撰寫任何程式碼。 每個範本都是針對特定控制項所設計,例如 Button 。
您通常會將範本宣告為 XAML 檔案區段上的 Resources
資源。 如同所有資源,套用範圍規則。
控制項範本比樣式更涉及。 這是因為控制項範本會重寫整個控制項的視覺外觀,而樣式只會將屬性變更套用至現有的控制項。 不過,由於控制項的範本是藉由設定 Control.Template 屬性來套用,因此您可以使用樣式來定義或設定範本。
設計工具通常可讓您建立現有範本的複本並加以修改。 例如,在 Visual Studio WPF 設計工具中,選取 CheckBox
控制項,然後按一下滑鼠右鍵,然後選取 [ 編輯範本 > 建立複本 ]。 此命令會產生定義 範本 的樣式。
<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual1}"/>
<Setter Property="Background" Value="{StaticResource OptionMark.Static.Background1}"/>
<Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border1}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid x:Name="markGrid">
<Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="1" Opacity="0" Stretch="None"/>
<Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="2" Opacity="0"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="true">
<Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual1}"/>
<Setter Property="Padding" Value="4,-1,0,0"/>
... content removed to save space ...
編輯範本複本是瞭解範本運作方式的絕佳方式。 您不必建立新的空白範本,而是更容易編輯複本並變更視覺效果簡報的幾個層面。
如需範例,請參閱 建立控制項 的範本。
TemplateBinding
您可能已經注意到上一節中定義的範本資源會使用 TemplateBinding 標記延伸 。 TemplateBinding
是範本案例的系結優化形式,類似于使用 {Binding RelativeSource={RelativeSource TemplatedParent}}
建構的系結。 TemplateBinding
適用于將範本的元件系結至 控制項的屬性。 例如,每個控制項都有 屬性 BorderThickness 。 TemplateBinding
使用 來管理此控制項設定會影響範本中的專案。
ContentControl 和 ItemsControl
ContentPresenter如果在 的 ContentControl 中 ControlTemplate 宣告 ,則 ContentPresenter 會自動系結至 ContentTemplate 和 Content 屬性。 同樣地, ItemsPresenter 中的 ControlTemplateItemsControl ,會自動系結至 ItemTemplate 和 Items 屬性。
DataTemplates
在此範例應用程式中,有一個 ListBox 控制項系結至相片清單。
<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>
這 ListBox 目前看起來如下。
大多數控制項都有某個型別的內容,而該內容通常來自您要繫結的資料。 在此範例中,該資料是相片清單。 在 WPF 中,您可以使用 DataTemplate 來定義資料的視覺表示。 基本上,您放入 DataTemplate 的內容會決定轉譯應用程式中的資料外觀。
在我們的範例應用程式中,每個自訂 Photo
物件都有一個 Source
字串類型的屬性,可指定影像的檔案路徑。 目前,相片物件是顯示成檔案路徑。
public class Photo
{
public Photo(string path)
{
Source = path;
}
public string Source { get; }
public override string ToString() => Source;
}
Public Class Photo
Sub New(ByVal path As String)
Source = path
End Sub
Public ReadOnly Property Source As String
Public Overrides Function ToString() As String
Return Source
End Function
End Class
若要讓相片顯示為影像,您可以建立 DataTemplate 作為資源。
<Window.Resources>
<!-- .... other resources .... -->
<!--DataTemplate to display Photos as images
instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
<Border Margin="3">
<Image Source="{Binding Source}"/>
</Border>
</DataTemplate>
</Window.Resources>
請注意, DataType 屬性類似于 TargetType 的 Style 屬性。 DataTemplate如果您的 位於 resources 區段中,當您將 屬性指定 DataType 為類型並省略 x:Key
時 DataTemplate ,就會在出現該類型時套用 。 您一律可以選擇使用 來指派 DataTemplate ,然後將它設定為 StaticResource
接受 DataTemplate 型別的屬性,例如 ItemTemplate 屬性或 ContentTemplate 屬性。 x:Key
基本上, DataTemplate 上述範例中的 會定義每當有 物件時 Photo
,它應該會顯示為 Image 中的 Border 。 有了這個 DataTemplate ,我們的應用程式現在看起來像這樣。
資料範本化模型還提供其他功能。 例如,如果您要使用 或 TreeView 之類的 Menu 類型顯示包含其他集合的 HierarchicalDataTemplate 集合 HeaderedItemsControl 資料,則 有 。 另一個資料範本化功能是 DataTemplateSelector ,可讓您根據自訂邏輯選擇 DataTemplate 要使用的 。 如需詳細資訊,請參閱資料範本化概觀,其中提供不同資料範本化功能的更深入探討。
觸發程序
觸發程序會在屬性值發生變更或在某個事件被引發時,設定屬性或啟動動作 (例如動畫)。 Style、 ControlTemplate 和 DataTemplate 都有一個 Triggers
屬性,可以包含一組觸發程式。 觸發程式有數種類型。
PropertyTriggers
設定 Trigger 屬性值或根據屬性值啟動動作的 ,稱為屬性觸發程式。
若要示範如何使用屬性觸發程式,除非選取屬性觸發程式,否則您可以讓每個 ListBoxItem 部分透明。 下列樣式會將 Opacity 的值 ListBoxItem 設定為 0.5
。 不過, IsSelected 當 屬性為 true
時,會 Opacity 設定為 1.0
。
<Window.Resources>
<!-- .... other resources .... -->
<Style TargetType="ListBoxItem">
<Setter Property="Opacity" Value="0.5" />
<Setter Property="MaxHeight" Value="75" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.Setters>
<Setter Property="Opacity" Value="1.0" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
這個範例會使用 Trigger 來設定 屬性值,但請注意,類別 Trigger 也具有 EnterActions 和 ExitActions 屬性,可讓觸發程式執行動作。
請注意, MaxHeight 的 ListBoxItem 屬性設定為 75
。 在下圖中,第三個專案是選取的專案。
EventTrigger 和分鏡腳本
另一種類型的觸發程式是 EventTrigger ,它會根據事件發生時啟動一組動作。 例如,下列 EventTrigger 物件會指定當滑鼠指標進入 ListBoxItem 時, MaxHeight 屬性會在第二個 0.2
期間內以動畫顯示 的值 90
。 當滑鼠指標從項目移開時,該屬性會在 1
秒的期間內恢復成原始值。 請注意,不需要指定 To 動畫的值 MouseLeave 。 這是因為動畫能夠記錄原始值。
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.Setters>
<Setter Property="Opacity" Value="1.0" />
</Trigger.Setters>
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetProperty="MaxHeight"
To="90" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:1"
Storyboard.TargetProperty="MaxHeight" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
如需詳細資訊,請參閱 分鏡腳本概觀 。
在下圖中,滑鼠指向第三個專案。
MultiTrigger、DataTrigger 及 MultiDataTrigger
除了 Trigger 和 EventTrigger 之外,還有其他類型的觸發程式。 MultiTrigger 可讓您根據多個條件來設定屬性值。 DataTrigger當您的條件的 屬性系結資料時,您可以使用 和 MultiDataTrigger 。
視覺狀態
控制項一律處於特定 狀態 。 例如,當滑鼠移至控制項介面上時,控制項會被視為 處於一般狀態 MouseOver
。 沒有特定狀態的控制項會被視為處於一般 Normal
狀態。 狀態會分成群組,且先前提到的狀態是狀態群組 CommonStates
的一部分。 大部分的控制項都有兩個狀態群組: CommonStates
和 FocusStates
。 在套用至控制項的每個狀態群組中,控制項一律處於每個群組的一個狀態,例如 CommonStates.MouseOver
和 FocusStates.Unfocused
。 不過,控制項不能位於相同群組內的兩個不同的狀態,例如 CommonStates.Normal
和 CommonStates.Disabled
。 以下是大部分控制項可辨識和使用的狀態資料表。
VisualState 名稱 | VisualStateGroup 名稱 | 描述 |
---|---|---|
正常 | CommonStates | 預設狀態。 |
MouseOver | CommonStates | 滑鼠指標移到控制項上。 |
按下 | CommonStates | 已按下控制項。 |
停用 | CommonStates | 已停用控制項。 |
焦點 | FocusStates | 控制項已取得焦點。 |
未取得焦點 | FocusStates | 控制項未取得焦點。 |
藉由在控制項範本的根項目上定義 System.Windows.VisualStateManager ,您可以在控制項進入特定狀態時觸發動畫。 會 VisualStateManager
宣告 和 VisualState 要 VisualStateGroup 監看的組合。 當控制項進入監看狀態時,會啟動 所 VisualStateManager
定義的動畫。
例如,下列 XAML 程式碼會 CommonStates.MouseOver
監看狀態,以動畫顯示名為 backgroundElement
的專案填滿色彩。 當控制項返回 CommonStates.Normal
狀態時,會還原名為 backgroundElement
的專案填滿色彩。
<ControlTemplate x:Key="roundbutton" TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal">
<ColorAnimation Storyboard.TargetName="backgroundElement"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="{TemplateBinding Background}"
Duration="0:0:0.3"/>
</VisualState>
<VisualState Name="MouseOver">
<ColorAnimation Storyboard.TargetName="backgroundElement"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="Yellow"
Duration="0:0:0.3"/>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
...
如需腳本的詳細資訊,請參閱 分鏡腳本概觀 。
共用資源和主題
典型的 WPF 應用程式可能會有多個在應用程式內套用的 UI 資源。 整體而言,這組資源可以視為應用程式的主題。 WPF 支援使用封裝為 類別的資源字典,將 UI 資源封裝為 ResourceDictionary 主題。
WPF 主題是使用 WPF 針對自訂任何元素視覺效果所公開的樣式和範本化機制來定義。
WPF 主題資源會儲存在內嵌資源字典中。 這些資源字典必須內嵌在已簽署的組件內,並且可內嵌在與程式碼本身相同的組件中,也可內嵌在並存的組件中。 對於 PresentationFramework.dll,包含 WPF 控制項的元件,主題資源位於一系列並存元件中。
當搜尋元素的樣式時,佈景主題會成為最後一個要查看的地方。 一般而言,搜尋一開始會先在元素樹狀結構中搜尋適當的資源,然後查看應用程式資源集合,最後查詢系統。 這可讓應用程式開發人員有機會在到達主題之前,重新定義樹狀結構或應用程式層級上任何物件的樣式。
您可以將資源字典定義為個別檔案,讓您能夠跨多個應用程式重複使用主題。 您也可以透過定義多個資源字典來提供類型相同但值不同的資源,以建立可切換的佈景主題。 在應用層級重新定義這些樣式或其他資源是建議用來縮小應用程式外觀的方法。
若要跨應用程式共用一組資源,包括樣式和範本,您可以建立 XAML 檔案,並定義 ResourceDictionary 包含檔案參考的 shared.xaml
。
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Shared.xaml" />
</ResourceDictionary.MergedDictionaries>
這是 的共用 shared.xaml
,其本身會 ResourceDictionary 定義包含一組樣式和筆刷資源的 ,讓應用程式中的控制項具有一致的外觀。
如需詳細資訊,請參閱 合併的資源字典 。
如果您要為自訂控制項建立主題,請參閱 控制項撰寫概觀 的主題 層級 定義資源一節。
另請參閱
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應