概念
对象资源是使用代码定义的一系列可以重用的对象,包括 画刷,样式,模板 等。WPF允许在代码中以及在xaml中各个位置定义对象资源。
资源集合
在FrameworkElement中定义了一个Resources属性,该属性使用Resource Dictionary类的实例填充,用于存储元素上的资源。根据我们之前学习的体系结构,定义在FrameworkElement也就表明了所有的WPF元素都具有资源的属性。
每个元素的子元素可以访问自己的和父元素的资源。所以一般我们将资源定义在窗口的资源属性中。
下面我们举例看一下资源的定义和使用
<Window.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
<StackPanel>
<Button Background="{StaticResource RedBrush}">测试1</Button>
</StackPanel>
</Grid>
资源层次
我们之前讲了,元素会从自己的资源属性和父元素的资源属性中查找资源进行使用,事实上窗口元素也不是最后一站,其查找层次是:
自己的资源 - 父元素的资源 - 应用程序资源 - 系统资源
其中应用程序资源和系统资源我们下面再将,我们可以先看一个问题:如果自己有一个资源,父元素中有一个同名的资源,会怎么样呢?
首先我们先问行不行,再看怎么样,能否同名呢?答案是能,下面的例子展示了这种情况
<Window.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
<StackPanel>
<Button>
<Button.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Blue"/>
</Button.Resources>
<Button.Background>
<StaticResource ResourceKey="RedBrush"/>
</Button.Background>
<Button.Content>
测试1
</Button.Content>
</Button>
</StackPanel>
</Grid>
上面代码在按钮的Resources中也定义了同名的Brush,最终运行结果是 蓝色,说明元素在碰到同名资源的时候优先使用自己的,换句话说,自己的资源会覆盖父元素的资源。
我们再看下面这段代码
<Window.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Grid>
<StackPanel>
<Button Background="{StaticResource RedBrush}">
<Button.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Blue"/>
</Button.Resources>
<Button.Content>
测试1
</Button.Content>
</Button>
</StackPanel>
</Grid>
运行结果是红色,说明资源必须是在使用之前定义的才能索引到,定义在使用之后的不能被查找到。
静态资源和动态资源
- 静态资源只加载一次,之后的变化不会响应
- 动态资源会随着资源的变化而变化
我们直接看一个例子,还是刚刚的例子,我们给Button设置一个Click事件,事件中写入下面的代码
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Resources["RedBrush"] = new SolidColorBrush(Colors.Blue);
}
我们测试发现,当我们使用
Background="{DynamicResource RedBrush}"
绑定的时候,按下按钮,按钮颜色变化为蓝色,当我们使用静态绑定则不会有任何颜色的变化。
两者对比
- 静态资源加载较快,在创建窗口的时候就加载完成了
- 动态资源消耗更大,在使用的时候才会加载
当我们碰到下面的情况的时候必须使用动态资源
- 资源依赖于系统的属性设置(主题颜色等)
- 需要使用代码替换资源的时候
由于两者加载时机的不同,在资源很大并且很复杂的情况之下,使用动态资源可以提升加载窗口的速度。
资源的共享与非共享
通常我们使用的资源都是共享的,即资源是一个对象,大家使用的都是这个对象,但是WPF同样允许我们使用非共享的对象,这个技术十分不常使用,大家了解即可
<SolidColorBrush x:Key="RedBrush" Color="Red" x:Shared="False"/>
使用代码访问资源
- 在拥有Resources属性的元素上使用 FindResource 方法查找资源
- 在拥有Resources属性的元素上使用 TryFindResource 方法查找资源,这个方法没有找到不会报异常
- 可以给元素的Resources属性调用Add方法添加资源
应用程序资源
我们可以在App.xaml中定义资源,我们之前学过,App.xaml中有一个Application元素,同样,我们可以在Application的Resources属性中添加资源。
应用程序资源用于在不同的窗口中共享资源。
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<SolidColorBrush x:Key="AppBrush" Color="AliceBlue"/>
</Application.Resources>
</Application>
系统资源
如果我们想要构建一个跟随系统主题颜色变化变化,跟随系统字体变化,跟随系统字号变化的软件,就必须使用系统资源。
系统资源其实就是系统提供的一系列静态的资源,有下面三个类
- SystemColors 系统颜色设置
- SystemFonts 字体设置
- SystemParameters 屏幕设置 鼠标设置 阴影 拖放等
如果不要求跟随系统设置变化实时变化,我们可以使用下面的方法进行系统资源的使用
<Button Background="{x:Static SystemColors.WindowTextBrush}">Test</Button>
或是在代码中使用
// 使用系统颜色
btn.Background = new SolidColorBrush(SystemColors.WindowTextColor);
// 使用系统画刷
btn.Background = SystemColors.WindowTextBrush;
这里如果我们需要系统变化的时候变化这个资源,就需要使用到动态绑定,明显这里动态绑定不是那么方便,如何做呢,我们使用下面的代码
<Button x:Name="btn" Background="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">Test</Button>
最后我们总结一下,静态的颜色资源都提供了三个版本
- 颜色版本,供我们构建画刷
- 画刷版本,可以直接使用
- Key版本,供我们动态绑定
资源字典 Resource Dictionary
上文中我们学习了对象资源,知道了资源集合,也知道了资源使用的优先级,查找的顺序,也知道了资源定义的几个位置以及系统内置的资源,但是我们都是现成的资源属性,我们之前讲了资源属性是由 Resource Dictionary 来构建的,它是一系列资源的集合,那我们就来学学 Resource Dictionary,学习了资源字典之后,我们就能将一系列的资源放到一个字典中,一起共享到别的项目中。
创建资源字典
创建资源字典我们可以像创建一个类一样,创建新建项,选择资源字典,系统就会帮我们创建一个资源字典,如下面的代码
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
</ResourceDictionary>
需要注意的是,资源文件的生成属性可以设置成Page或者Resource都可以。
使用资源字典
-
将本项目中的资源文件共享到项目中所有地方(修改App.xaml)
<Application x:Class="StyleStudy.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:StyleStudy" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Generic.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
在项目中的其他地方,可以直接使用这个资源
<TextBlock Text="{StaticResource nameStr}" FontWeight="{StaticResource fontWeight}"></TextBlock>
-
引用别的项目中的资源
要想引用别的项目中的资源首先需要引用资源所在项目,然后下面介绍两种引用的方法
// 使用代码引用别的项目中的资源(只能用在cs代码中) ResourceDictionary resource = new ResourceDictionary(); resource.Source = new Uri("Views;component/Brushes.xaml", UriKind.Relative); // resource指向了资源文件,使用字典的取值语法就可以获取相应的资源 textBox.Foreground = (SolidColorBrush)resource["MainColor"];
// xaml中引入别的项目的资源文件 <Application x:Class="StyleStudy.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:StyleStudy" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Generic.xaml"></ResourceDictionary> <ResourceDictionary Source="/StyleStudy.Views;component/Themes/Default/Generic.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
注意要使用MergedDictionaries就可以将资源字典整个提升到项目级别在多窗口中共享,结合我们之前讲的Uri的语法,就可以Merge其他项目的资源字典,这个在多项目的解决方案中十分有用。
在不同程序集之间共享资源还有一种办法就是使用ComponentResourceKey,这种方法相较于上面两种办法来说要复杂的多,我们这里就不详细讲了,感兴趣的同学可以自行查找相关资料进行学习,然而,掌握了上面讲的方法在实际的开发中就完全够用了,尤其是MergedDictionaries一定要理解吃透。