一、三个基础知识
1、分层式资源 Hierarchical Resource
FrameworkElement基类有一个Resources属性,类型是ResourceDictionary。这个字典包含了一组键值对,每个键值对中的key和value都可以使任意的object。
Resource dictionaries 在程序中分层存放。当程序需要定位一个资源(例如Brush, Style,DataTemplate或其他对象)的时候,首先他检查发起资源寻找请求的元素的Resource属性。假如没有找到,它会检查该元素的父元素。假如还是没有找到该资源,那么它会一直向上,检查每个祖先节点。假如仍然没有找到,那么它最后会问Application,它是否有这个resource。再没找到,就会抛出异常;
2、合并的资源字典 Merged Resource Dictionaries
ResourceDictionary 包含一个MergedDictionaries属性,其类型是Collection
3、Dynamic Resource Reference
动态资源与静态资源引用的区别;静态资源引用,是在程序加载的时候读取一次,后面资源变更了,是不是会再去读取资源的;而动态资源引用,除了在程序加载时读取外,如果资源发生了改变,程序会再次自动读取;
详情这里:“https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/dynamicresource-markup-extension” 有更多的关于编写此代码的信息。
注意几点:
- 1)理论上ResourceDictionary中的KEY不应该是重复的;但是使用MergedDictionary合并的资源,它们中的KEY名称重复是没有关系的;
- 2)资源寻找会返回MergedDictionary中最后一个与key对应的资源;这里我们要注意MergedDictionary的合并顺序。若MergedDictionary在--xaml中定义,那么MergedDictionary的顺序就是xaml中子元素的顺序。假如一个key在ResourceDictionary中被定义了,同时又出现在一个被合并的子典中,那么查找过程会返回ResourceDictionary中的对应资源。这个规则对于静态资源引用和动态资源引用都适用。详细了解,可查看https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/merged-resource-dictionaries
- 3)在MergedDictionary中的资源会在资源查找的过程中占用一段空间,这段空间位于合并它们的资源之后
二、实现换肤步骤
1、新建WPF工程
<Window x:Class="WPFView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFView"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="Button1"></Button>
<Button Grid.Row="1" Grid.Column="1" Content="Button2"></Button>
</Grid>
</Window>
2、通过Style设置元素的样式
需要支持动态换肤的元素,通过Style设置元素的样式;
通过编辑模板的方式,创建一个样式
点击后,默认会生成在window.Resource下面;
3、将需要换肤的部分,提取出来保存成ResouceDictionary
在Window.Resource中; 把常量资源,单独提取出来,保存至新创建的资源字典;(新建ResourceDictionary可以通过右键快捷菜单,选择【添加】-【资源字典】的方式创建)
BlackBrush.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
</ResourceDictionary>
为了演示换肤,我们将BlackBrush.xaml复制一份,存为RedBrush.xaml;
将按钮背景改成红色
将Window.Resource剩余部分,并保存至单独的ResouceDictionary,文件名为 Theme.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="FocusVisual">
<!--这里省略掉-->
</Setter>
</Style>
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<!--这里省略掉-->
</Style>
</ResourceDictionary>
至此,我们已经为需要换肤的按钮,1)使用样式来修改外观 2)将样式的常量部分提取出来,生成两个资源字典文件BlackBrush.xaml,RedBrush.xaml 3)将样式Style单独提取出来,生成Theme.xaml;
4、动态引用资源
元素设置样式时,引用style中的资源时,应采用DynamicResource方式使用资源;这样我们就可以在运行时通过更改主题来让这些元素应用新的主题资源了。
<Window x:Class="WPFView.MainWindow"
<!--这里省略掉部分命名空间-->
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<!--将资源文件合并进来-->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme.xaml"/>
<ResourceDictionary Source="/Theme/RedBrush.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--按钮的样式动态引用资源-->
<Button Grid.Row="0" Grid.Column="0" Content="Button1" Style="{DynamicResource ButtonStyle1}"></Button>
<Button Grid.Row="1" Grid.Column="1" Content="Button2" Style="{DynamicResource ButtonStyle1}"></Button>
</Grid>
</Window>
5、手动换肤
修改<Window.Resource>资源,将RedBrush.xaml修改为:BlackBrush.xaml可以实现换肤