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