"Consistency in a user interface is an important trait; there are many facets of consistency,
one of which is the consistent look and feel of controls. For example, all buttons should
look roughly the same – similar colors, the same margins, and so on."
UI的風格一致性是應用程序應當關注的重要特性。
“Styles provide a convenient way to group a set of properties (and triggers) under a single
object, and then selectively (or automatically as we'll see later) apply it to elements.”
1.Creating and using styles
用一個Demo,來總結Style。
MainWindow.xaml如下:
<Window x:Class="CreatingAndUsingStyle.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Styled Calculatot" Height="269" Width="180" ResizeMode="CanMinimize"> <Window.Resources> <Style TargetType="Button" x:Key="numericStyle"> <Setter Property="FontSize" Value="20" /> <Setter Property="Margin" Value="4" /> <Setter Property="Padding" Value="6" /> <Setter Property="Effect"> <Setter.Value> <DropShadowEffect Color="Blue"/> </Setter.Value> </Setter> </Style> <Style TargetType="Button" x:Key="operatorStyle" BasedOn="{StaticResource numericStyle}"> <Setter Property="FontWeight" Value="ExtraBold" /> <Setter Property="Effect"> <Setter.Value> <DropShadowEffect Color="Red" /> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBox Background="Cyan" IsReadOnly="True" Grid.ColumnSpan="4"/> <Button Content="7" Grid.Row="1" Style="{StaticResource numericStyle}"/> <Button Content="8" Grid.Row="1" Grid.Column="1" Style="{StaticResource numericStyle}"/> <Button Content="9" Grid.Row="1" Grid.Column="2" Style="{StaticResource numericStyle}"/> <Button Content="4" Grid.Row="2" Style="{StaticResource numericStyle}"/> <Button Content="5" Grid.Row="2" Grid.Column="1" Style="{StaticResource numericStyle}"/> <Button Content="6" Grid.Row="2" Grid.Column="2" Style="{StaticResource numericStyle}"/> <Button Content="1" Grid.Row="3" Style="{StaticResource numericStyle}"/> <Button Content="2" Grid.Row="3" Grid.Column="1" Style="{StaticResource numericStyle}"/> <Button Content="3" Grid.Row="3" Grid.Column="2" Style="{StaticResource numericStyle}"/> <Button Content="0" Grid.Row="4" Style="{StaticResource numericStyle}"/> <Button Content="=" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" Style="{StaticResource operatorStyle}"> <Button.Effect> <DropShadowEffect Color="Green" /> </Button.Effect> </Button> <Button Content="+" Grid.Row="4" Grid.Column="3" Style="{StaticResource operatorStyle}"/> <Button Content="-" Grid.Row="3" Grid.Column="3" Style="{StaticResource operatorStyle}"/> <Button Content="X" Grid.Row="2" Grid.Column="3" Style="{StaticResource operatorStyle}"/> <Button Content="/" Grid.Row="1" Grid.Column="3" Style="{StaticResource operatorStyle}"/> </Grid> </Window>
效果如下:
Point of Interest:
1.“A Style is a container for a bunch of Setter objects (and triggers, as we'll see later). Each
setter indicates which property should have which value; the property must be a dependency
property. The FrameworkElement class exposes a Style property that can be set to such a
Style object. Styles are always built as resources.”
Setter的Property必須是依賴屬性;
FrameworkStyle暴露了Style這個屬性;
Style一般放在Resources中.
2."The TargetType property of a Style is typically set, which makes the Style applicable to
that particular type (this can be any type, even a type of a custom control) and any derived
types."
TargetType="Button" 指定Style應用的類型。
一般,我們使用Style的時候,我們都會設置這個(In practice ,TargetType is always specified.),想想為什么?
3. x:Key="numericStyle" 可以根據需要進行有或無。
a.當其有的時候,我們需要對需要 Style="{StaticResource numericStyle}" .
b.其不設置的時候,默認所有TargetType使用此Style。 見2.
4. BasedOn="{StaticResource numericStyle}" 這是Style的繼承。
Style繼承時,可以修改BaseOn Style的Setter。如operatorStyle的 部分所示。
Style繼承很好用,但是要注意:基Style的修改會影響子style.
5.“An element that uses a style can change a property that is set explicitly by a Style (a local
value), and this is stronger than a Style property setter.”
對於應用了Style的element,我們可以設置其屬性值,且這個優先級更高(覆蓋Style設置)。如"="Button的 部分所示。
2.Applying a style automatically
我們移除 x:Key="numericStyle"。讓所有Button使用這個style.
MainWindow.xaml修改如下:
<Window x:Class="CreatingAndUsingStyle.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Styled Calculatot" Height="269" Width="180" ResizeMode="CanMinimize"> <Window.Resources> <Style TargetType="Button"> <Setter Property="FontSize" Value="20" /> <Setter Property="Margin" Value="4" /> <Setter Property="Padding" Value="6" /> <Setter Property="Effect"> <Setter.Value> <DropShadowEffect Color="Blue"/> </Setter.Value> </Setter> </Style> <Style TargetType="Button" x:Key="operatorStyle" BasedOn="{StaticResource {x:Type Button}}"> <Setter Property="FontWeight" Value="ExtraBold" /> <Setter Property="Effect"> <Setter.Value> <DropShadowEffect Color="Red" /> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBox Background="Cyan" IsReadOnly="True" Grid.ColumnSpan="4"/> <Button Content="7" Grid.Row="1" /> <Button Content="8" Grid.Row="1" Grid.Column="1" /> <Button Content="9" Grid.Row="1" Grid.Column="2" /> <Button Content="4" Grid.Row="2" /> <Button Content="5" Grid.Row="2" Grid.Column="1" /> <Button Content="6" Grid.Row="2" Grid.Column="2" /> <Button Content="1" Grid.Row="3" /> <Button Content="2" Grid.Row="3" Grid.Column="1" /> <Button Content="3" Grid.Row="3" Grid.Column="2" /> <Button Content="0" Grid.Row="4" Style="{x:Null}"/> <Button Content="=" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" Style="{StaticResource operatorStyle}"> <Button.Effect> <DropShadowEffect Color="Green" /> </Button.Effect> </Button> <Button Content="+" Grid.Row="4" Grid.Column="3" Style="{StaticResource operatorStyle}"/> <Button Content="-" Grid.Row="3" Grid.Column="3" Style="{StaticResource operatorStyle}"/> <Button Content="X" Grid.Row="2" Grid.Column="3" Style="{StaticResource operatorStyle}"/> <Button Content="/" Grid.Row="1" Grid.Column="3" Style="{StaticResource operatorStyle}"/> </Grid> </Window>
效果如下:
Point of Interest
1.“Automatic styles are created as resources without a key. This does not mean there is no key,
because it's still a dictionary. The key becomes the actual type to apply the style to defined by
the TargetType property.”
不為Style指定x:key,這個Style將應用於所有的x:Tyle element;
沒有指定x:key,不代表其沒有key,其key由x:Tyle定義,就本例而言是{x:Type Button}},從Style繼承中(xaml中綠色高亮代碼),可見一斑。
2.如果,這樣的Style被Set在Window的Resource,則影響這個Window上的所有x:Type類型的element;
如果被set在application's resources,則影響所有window。
3.“The Style is applied to all elements of the target type, but not derived types.
Any element that does not set its style explicitly obtains that style automatically. If an element
wishes to revert to its default style, it can set its Style property to null ( {x:Null} in XAML)
or set its Style to another named style.”
如果,我們希望某個element不應用這個style,我們可以設置它的Style屬性為其他style;
如果想讓它保持默認的樣子,我們可以設置其Style="{x:Null}",如Button “0”。
3.動態更換Style
“Automatic styles are a great way to create a consistent look and feel without burdening the
developer (or the designer) with the details of the various visual properties. It can also be
used as a quick way to implement skinning."
在Skin文件夾中添加兩個Resource Dictionary,如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Style TargetType="TextBox"> <Setter Property="Background" Value="LightBlue" /> <Setter Property="Foreground" Value="Black" /> <Setter Property="BorderThickness" Value="2" /> <Setter Property="BorderBrush" Value="DarkBlue" /> <Style.Triggers> <Trigger Property="IsReadOnly" Value="True"> <Setter Property="Background" Value="Blue" /> </Trigger> </Style.Triggers> </Style> <Style TargetType="Button"> <Setter Property="Background" Value="Cyan" /> </Style> </ResourceDictionary>

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Style TargetType="TextBox"> <Setter Property="Background" Value="Violet" /> <Setter Property="Foreground" Value="DarkViolet" /> <Setter Property="BorderThickness" Value="2" /> <Setter Property="BorderBrush" Value="LightPink" /> <Style.Triggers> <Trigger Property="IsReadOnly" Value="True"> <Setter Property="Background" Value="Pink" /> </Trigger> </Style.Triggers> </Style> <Style TargetType="Button"> <Setter Property="Background" Value="Red" /> </Style> </ResourceDictionary>
並設置其Property為:Content/Aways Copy.
“These settings prevent the default compilation to BAML and also provide the flexibility to
change the skins without recompilation. ”
請參考DebugLZQ前面的博文:WPF整理-二進制資源和內容
MainWindow.xaml,MainWindow.xaml.cs如下:
<Window x:Class="WPFStyleTriggerAndControlTemplate.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="303.719" Width="530.785"> <Grid Margin="0,0,2,0"> <TextBox HorizontalAlignment="Left" Height="23" Margin="10,35,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/> <TextBox HorizontalAlignment="Left" Height="23" Margin="156,35,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/> <TextBox HorizontalAlignment="Left" Height="23" Margin="293,35,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120" RenderTransformOrigin="1.577,0.303"/> <Button Content="Button" HorizontalAlignment="Left" Margin="433,35,0,0" VerticalAlignment="Top" Width="75"/> <GroupBox FontSize="28" Header="Select Skin" Margin="75,102,62,0" VerticalAlignment="Top" Height="158"> <StackPanel Orientation="Vertical" HorizontalAlignment="Center"> <RadioButton Content="Normal" Checked="AllRadioButtonChecked"/> <RadioButton Content="Blue Skin" Checked="AllRadioButtonChecked"/> <RadioButton Content="Red Skin" Checked="AllRadioButtonChecked"/> </StackPanel> </GroupBox> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WPFStyleTriggerAndControlTemplate { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void AllRadioButtonChecked(object sender, RoutedEventArgs e) { switch (((RadioButton)sender).Content.ToString()) { case "Blue Skin": ChangeSkin("Skins/BlueSkin.xaml"); break; case "Red Skin": ChangeSkin("Skins/RedSkin.xaml"); break; case "Normal": Application.Current.Resources.MergedDictionaries.Clear(); break; } } void ChangeSkin(string skinRelativeUri) { var si = Application.GetContentStream(new Uri(skinRelativeUri, UriKind.Relative)); var rd = (ResourceDictionary)XamlReader.Load(si.Stream); Application.Current.Resources.MergedDictionaries.Clear(); Application.Current.Resources.MergedDictionaries.Add(rd); } } }
運行效果如下:
4.Other places to set styles
"The example shows styles applied to the FrameworkElement.Style property, but this is
not the only property that can accept a Style.
Other examples include the following:"
"FrameworkElement.FocusVisualStyle property: Accepts a Style that affects
the way the focus indicator is rendered when that element has the keyboard focus."
"ItemsControl.ItemContainerStyle property: Accepts a Style that affects the
container element of each data item (for example, ListBoxItem for a ListBox)."
"DataGrid.CellStyle: Accepts a Style that affects the way a cell is rendered.
Similar properties exposed by the DataGrid include ColumnHeaderStyle,
DragIndicatorStyle, DropLocationIndicatorStyle, RowHeaderStyle,
and RowStyle."
5.Style和ControlTemplate
ControlTemplate一般有如下2種使用方法
方法1:
<Window.Resources>
<ControlTemplate x:Key="AnimatedExpanderButtonTemp" TargetType="{x:Type ToggleButton}">
Template="{StaticResource RevealExpanderTemp}"
--------------
方法2:
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
-----------------------------
關於DataTemplate請參考DebugLZQ前面的博文:WPF整理-使用 DataTemplate
6.Style & Trigger & ControlTemplate一個非常常見的例子
在實際應用中,這3個一般會同時用到。我們用一個自定義的最大化/最小化/關閉按鈕等,如下:
一般有2種實現方法,第一種方法如下:
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="362.767" Width="524.468" WindowStyle="None" AllowsTransparency="True" Background="Transparent"> <Window.Resources> <Style TargetType="Button" x:Key="btnStyle1"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="grid1" Background="Transparent"> <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" RecognizesAccessKey="True"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="grid1" Property="Grid.Background" Value="Red"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <ControlTemplate TargetType="Button" x:Key="btnTemplate1"> <Grid x:Name="grid1" Background="Transparent" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"> <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" RecognizesAccessKey="True"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="grid1" Property="Grid.Background" Value="LightBlue"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Window.Resources> <Grid > <Grid.Background> <ImageBrush ImageSource="Button_Icons/background_mainwnd.png"/> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" FlowDirection="RightToLeft"> <Button Style="{StaticResource btnStyle1}" Content="X" Foreground="White" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Width="23" Height="23" Margin="5,0,5,0"/> <Button Template="{StaticResource btnTemplate1}" Content="口" Foreground="White" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Width="23" Height="23" Margin="5,0,5,0"/> <Button Template="{StaticResource btnTemplate1}" Content="一" Foreground="White" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Width="23" Height="23" Margin="5,0,5,0"/> </StackPanel> </Grid> </Window>
效果如下:
這種方法的缺點是,我們設置的Content比較粗糙~
第二中方法,借助相關的圖標的png圖片及相應的效果圖片,來設置Content,原理同上
<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="362.767" Width="524.468" WindowStyle="None" AllowsTransparency="True" Background="Transparent"> <Window.Resources> <Style TargetType="Button" x:Key="btnStyle1"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid Background="Transparent"> <ContentPresenter x:Name="cp1" Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" RecognizesAccessKey="True"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="cp1" Property="Content"> <Setter.Value> <Image Source="Button_Icons/xm.png"/> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="Button" x:Key="btnStyle2"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid Background="Transparent"> <ContentPresenter x:Name="cp1" Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" RecognizesAccessKey="True"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="cp1" Property="Content"> <Setter.Value> <Image Source="Button_Icons/mxm.png"/> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="Button" x:Key="btnStyle3"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid Background="Transparent"> <ContentPresenter x:Name="cp1" Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center" RecognizesAccessKey="True"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="cp1" Property="Content"> <Setter.Value> <Image Source="Button_Icons/mim.png"/> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid > <Grid.Background> <ImageBrush ImageSource="Button_Icons/background_mainwnd.png"/> </Grid.Background> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal" FlowDirection="RightToLeft"> <Button Style="{StaticResource btnStyle1}" Foreground="White" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Width="30" Height="30" > <Button.Content> <Image Source="Button_Icons/xn.png" /> </Button.Content> </Button> <Button Style="{StaticResource btnStyle2}" Foreground="White" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Width="30" Height="30" > <Button.Content> <Image Source="Button_Icons/mxn.png" /> </Button.Content> </Button> <Button Style="{StaticResource btnStyle3}" Foreground="White" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Top" Width="30" Height="30" > <Button.Content> <Image Source="Button_Icons/min.png" /> </Button.Content> </Button> </StackPanel> </Grid> </Window>
效果如下:
Style的另一個非常常用的場景是:視圖切換。參考DebugLZQ前面總結DataTemplate的博文中的一個例子:6.DataTemplate的一個非常常見的應用
請關注后續整理~
Wish it helps~