WPF之换肤


WPF之换肤

设计原理

WPF换肤的设计原理,利用资源字典为每种皮肤资源添加不同的样式,在后台切换皮肤资源文件。

截图

上图中,第一张图采用规则样式,第二张图采用不规则样式,截图的时候略有瑕疵。

资源字典

规则样式资源Skin.RegularStyle.xaml

  1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  3 
  4     
  5     <Style x:Key="WindowStyle" TargetType="Window">
  6         <Setter Property="Template">
  7             <Setter.Value>
  8                 <ControlTemplate TargetType="Window">
  9                     <Border BorderBrush="{TemplateBinding BorderBrush}" 
 10                             BorderThickness="{TemplateBinding BorderThickness}">
 11                         <Border.Background>
 12                             <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
 13                                 <GradientStop Color="Green" Offset="0">GradientStop>
 14                                 <GradientStop Color="LightGreen" Offset="0.4">GradientStop>
 15                                 <GradientStop Color="White" Offset="1">GradientStop>
 16                             LinearGradientBrush>
 17                         Border.Background>
 18                         <ContentPresenter>ContentPresenter>
 19                     Border>
 20                 ControlTemplate>
 21             Setter.Value>
 22         Setter>
 23     Style>
 24 
 25     
 26     <Style TargetType="Button">
 27         <Setter Property="Width" Value="70">Setter>
 28         <Setter Property="Height" Value="23">Setter>
 29         <Setter Property="Template">
 30             <Setter.Value>
 31                 <ControlTemplate TargetType="Button">
 32                     <Border Name="bdr" Cursor="Arrow"
 33                             BorderBrush="{TemplateBinding BorderBrush}" 
 34                             BorderThickness="{TemplateBinding BorderThickness}">
 35                         <Border.Background>
 36                             <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
 37                                 <GradientStop Color="White" Offset="0">GradientStop>
 38                                 <GradientStop Color="LightGreen" Offset="0.3">GradientStop>
 39                                 <GradientStop Color="Green" Offset="1">GradientStop>
 40                             LinearGradientBrush>
 41                         Border.Background>
 42                         <TextBlock Name="tbk" Background="Transparent" Foreground="DarkGreen" TextAlignment="Center"
 43                                    Text="{TemplateBinding Content}">TextBlock>
 44                     Border>
 45                     <ControlTemplate.Triggers>
 46                         <Trigger Property="IsMouseOver" Value="True">
 47                             <Setter TargetName="bdr" Property="Background">
 48                                 <Setter.Value>
 49                                     <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
 50                                         <GradientStop Color="LightGreen" Offset="0">GradientStop>
 51                                         <GradientStop Color="Green" Offset="1">GradientStop>
 52                                     LinearGradientBrush>
 53                                 Setter.Value>
 54                             Setter>
 55                             <Setter TargetName="tbk" Property="Foreground" Value="White">Setter>
 56                         Trigger>
 57                     ControlTemplate.Triggers>
 58                 ControlTemplate>
 59             Setter.Value>
 60         Setter>
 61     Style>
 62 
 63     
 64     <Style TargetType="TextBox">
 65         <Setter Property="FontFamily" Value="SketchFlow Print"/>
 66         <Setter Property="FontSize" Value="14"/>
 67         <Setter Property="Template">
 68             <Setter.Value>
 69                 <ControlTemplate TargetType="TextBox">
 70                     <Border BorderBrush="DarkGreen" BorderThickness="0.5">
 71                         <ScrollViewer x:Name="PART_ContentHost" Focusable="false" 
 72                                       HorizontalScrollBarVisibility="Hidden" 
 73                                       VerticalScrollBarVisibility="Hidden">ScrollViewer>
 74                     Border>
 75                 ControlTemplate>
 76             Setter.Value>
 77         Setter>
 78     Style>
 79 
 80     
 81     <Style TargetType="ContextMenu">
 82         <Setter Property="Template">
 83             <Setter.Value>
 84                 <ControlTemplate TargetType="ContextMenu">
 85                     <Border BorderBrush="Green" BorderThickness="1">
 86                         <ItemsPresenter/>
 87                     Border>
 88                 ControlTemplate>
 89             Setter.Value>
 90         Setter>
 91     Style>
 92 
 93     
 94     <Style TargetType="MenuItem">
 95         <Setter Property="Template">
 96             <Setter.Value>
 97                 <ControlTemplate TargetType="MenuItem">
 98                     <Border Name="border" Background="LightGreen" BorderThickness="0">
 99                         <TextBlock Name="tbk" Background="Transparent" Padding="5,5"
100                                    Text="{TemplateBinding Header}">TextBlock>
101                     Border>
102                     <ControlTemplate.Triggers>
103                         <Trigger Property="IsMouseOver" Value="True">
104                             <Setter TargetName="border" Property="Background" Value="Green">Setter>
105                             <Setter TargetName="tbk" Property="Foreground" Value="White">Setter>
106                         Trigger>
107                     ControlTemplate.Triggers>
108                 ControlTemplate>
109             Setter.Value>
110         Setter>
111     Style>
112 
113     
114     <Style TargetType="TextBlock">
115         <Setter Property="FontFamily" Value="SketchFlow Print"/>
116         <Setter Property="FontSize" Value="14"/>
117     Style>
118 
119 ResourceDictionary>

不规则样式资源Skin.RoundedCornerStyle.xaml

  1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  3 
  4     
  5     <Style x:Key="WindowStyle" TargetType="Window">
  6         <Setter Property="Template">
  7             <Setter.Value>
  8                 <ControlTemplate TargetType="Window">
  9                     <Grid Margin="10">
 10                         <Rectangle Fill="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"  
 11                                    RadiusX="5" RadiusY="5">
 12                             <Rectangle.Effect>
 13                                 <DropShadowEffect BlurRadius="10" Color="Black" Direction="0" Opacity="0.8"
 14                                                   RenderingBias="Performance" ShadowDepth="0"/>
 15                             Rectangle.Effect>
 16                         Rectangle>
 17                         <Border BorderBrush="{TemplateBinding BorderBrush}"  
 18                                 BorderThickness="{TemplateBinding BorderThickness}" 
 19                                 SnapsToDevicePixels="True" CornerRadius="5">
 20                             <Border.Background>
 21                                 <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
 22                                     <GradientStop Color="Blue" Offset="0">GradientStop>
 23                                     <GradientStop Color="LightBlue" Offset="0.4">GradientStop>
 24                                     <GradientStop Color="White" Offset="1">GradientStop>
 25                                 LinearGradientBrush>
 26                             Border.Background>
 27                             <ContentPresenter>ContentPresenter>
 28                         Border>
 29                     Grid>
 30                 ControlTemplate>
 31             Setter.Value>
 32         Setter>
 33     Style>
 34     
 35     
 36     <Style TargetType="Button">
 37         <Setter Property="Width" Value="70">Setter>
 38         <Setter Property="Height" Value="23">Setter>
 39         <Setter Property="Template">
 40             <Setter.Value>
 41                 <ControlTemplate TargetType="Button">
 42                     <Border Name="bdr" CornerRadius="5" Cursor="Hand"
 43                             BorderBrush="{TemplateBinding BorderBrush}"  
 44                             BorderThickness="{TemplateBinding BorderThickness}">
 45                         <TextBlock Name="tbk" Background="Transparent" Foreground="Yellow"  TextAlignment="Center"
 46                                    Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}">TextBlock>
 47                         <Border.Background>
 48                             <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
 49                                 <GradientStop Color="White" Offset="0">GradientStop>
 50                                 <GradientStop Color="LightBlue" Offset="0.3">GradientStop>
 51                                 <GradientStop Color="Blue" Offset="1">GradientStop>
 52                             LinearGradientBrush>
 53                         Border.Background>
 54                     Border>
 55                     <ControlTemplate.Triggers>
 56                         <Trigger Property="IsMouseOver" Value="True">
 57                             <Setter TargetName="bdr" Property="Background">
 58                                 <Setter.Value>
 59                                     <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
 60                                         <GradientStop Color="LightBlue" Offset="0">GradientStop>
 61                                         <GradientStop Color="Blue" Offset="1">GradientStop>
 62                                     LinearGradientBrush>
 63                                 Setter.Value>
 64                             Setter>
 65                             <Setter TargetName="tbk" Property="Foreground" Value="LightYellow">Setter>
 66                         Trigger>
 67                     ControlTemplate.Triggers>
 68                 ControlTemplate>
 69             Setter.Value>
 70         Setter>
 71     Style>
 72 
 73     
 74     <Style TargetType="TextBox">
 75         <Setter Property="FontFamily" Value="Times New Roman">Setter>
 76         <Setter Property="FontSize" Value="14">Setter>
 77         <Setter Property="Template">
 78             <Setter.Value>
 79                 <ControlTemplate TargetType="TextBox">
 80                     <Border BorderBrush="Blue" BorderThickness="0.5" CornerRadius="5">
 81                         <ScrollViewer x:Name="PART_ContentHost" Focusable="false" 
 82                                       HorizontalScrollBarVisibility="Hidden" 
 83                                       VerticalScrollBarVisibility="Hidden">ScrollViewer>
 84                     Border>
 85                 ControlTemplate>
 86             Setter.Value>
 87         Setter>
 88     Style>
 89 
 90     
 91     <Style TargetType="ContextMenu">
 92         <Setter Property="Template">
 93             <Setter.Value>
 94                 <ControlTemplate TargetType="ContextMenu">
 95                     <Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
 96                         <ItemsPresenter/>
 97                     Border>
 98                 ControlTemplate>
 99             Setter.Value>
100         Setter>
101     Style>
102 
103     
104     <Style TargetType="MenuItem">
105         <Setter Property="Template">
106             <Setter.Value>
107                 <ControlTemplate TargetType="MenuItem">
108                     <Border Name="border" Background="LightSkyBlue" BorderThickness="0" CornerRadius="5">
109                         <TextBlock Name="tbk" Background="Transparent" Padding="5,5"
110                                    Text="{TemplateBinding Header}">TextBlock>
111                     Border>
112                     <ControlTemplate.Triggers>
113                         <Trigger Property="IsMouseOver" Value="True">
114                             <Setter TargetName="border" Property="Background" Value="BlueViolet">Setter>
115                             <Setter TargetName="tbk" Property="Foreground" Value="White">Setter>
116                         Trigger>
117                     ControlTemplate.Triggers>
118                 ControlTemplate>
119             Setter.Value>
120         Setter>
121     Style>
122     
123     
124     <Style TargetType="TextBlock">
125         <Setter Property="FontFamily" Value="Times New Roman"/>
126         <Setter Property="FontSize" Value="14"/>
127     Style>
128 ResourceDictionary>

  仔细观察上面定义的样式,你会发现在定义Window样式的时候指定了Key,其他的Control样式却没有指定Key。大家都知道,如果没有给Style指定Key,那么这个Style会应用到所有目标类型(TargetType)为指定类型的Control。请看下面一段文字:

因为在换肤的过程中,需要动态加载Window的样式,所以用DynamicResource作绑定Style="{DynamicResource WindowStyle}"。

App.xaml

程序运行的时候,默认加载规则样式的皮肤。

1     <Application.Resources>
2         <ResourceDictionary>
3             <ResourceDictionary.MergedDictionaries>
4                 <ResourceDictionary Source="Dictionary\Skin.RegularStyle.xaml">ResourceDictionary>
5             ResourceDictionary.MergedDictionaries>
6         ResourceDictionary>
7     Application.Resources>

后台代码

 1         /// 
 2         /// MenuItem的执行方法
 3         /// 
 4         /// 
 5         private void RelayMenuItemEvent(object parameter)
 6         {
 7             if (parameter.ToString() == RegularStyle)
 8             {
 9                 ChangeSkinResource(Skins[0]);
10             }
11             else if (parameter.ToString() == RoundedCornerStyle)
12             {
13                 ChangeSkinResource(Skins[1]);
14             }
15         }
16 
17         /// 
18         /// 更换皮肤资源
19         /// 
20         /// 
21         private void ChangeSkinResource(ResourceDictionary skin)
22         {
23             if (Application.Current.Resources.MergedDictionaries[0].Source.IsAbsoluteUri)
24             {
25                 if (Application.Current.Resources.MergedDictionaries[0].Source.OriginalString != skin.Source.OriginalString)
26                 {
27                     Application.Current.Resources.MergedDictionaries[0] = skin;
28                 }
29             }
30             else
31             {
32                 if (Application.Current.Resources.MergedDictionaries[0].Source.OriginalString.ToString('\\') != skin.Source.OriginalString.ToString('/'))
33                 {
34                     Application.Current.Resources.MergedDictionaries[0] = skin;
35                 }
36             }
37         }

运行的时候在MainWindow上右键选择皮肤样式,就可以换肤了。

源码下载

链接

stackoverflow