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 <!--Window样式--> 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 <!--Button样式--> 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 <!--TextBox样式--> 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 <!--ContextMenu样式--> 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 <!--MenuItem样式--> 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 <!--TextBlock样式--> 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 <!--Window样式--> 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 <!--Button样式--> 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 <!--TextBox样式--> 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 <!--ContextMenu样式--> 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 <!--MenuItem样式--> 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 <!--TextBlock样式--> 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 /// <summary> 2 /// MenuItem的执行方法 3 /// </summary> 4 /// <param name="parameter"></param> 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 /// <summary> 18 /// 更换皮肤资源 19 /// </summary> 20 /// <param name="skin"></param> 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上右键选择皮肤样式,就可以换肤了。