10 WPF资源
资源是保持有用对象在附近的一种方法。诸如:刷子,样式,或模板对象。因此,你能更容易地重用他们。
尽管能用代码创造和操纵资源,通常用XAML标记。一个资源被定义以后,你能使用它,在窗口中,标记的剩余部分(或,在一个应用资源的情况下,遍及你的应用的剩余部分)。这技术简化你的标记,节省重复的编码,并且允许你存储用户界面细节(诸如你的应用的颜色方案)在一个集中的地方因而他们能被容易地修改。对象资源也是重用WPF样式基础。
资源基础
你定义资源的位置可以在代码中、或在标记的各种地方(除了特殊控件、特殊窗口、或者整个应用程序)。
资源的好处包括:效率高,可维护性,适应性。
资源集合
每个元素包括一个Resources属性,存储一个资源的词典集合。是ResourceDictionary类的一个实例。资源集合能持有任何类型的对象,由字符串索引。
尽管每个元素包括Resources属性(定义在FrameworkElement类),最常在窗口元素上定义资源。那是因为每个元素首先访问自己的资源集合,然后是它父元素的资源集合。
例如,考虑带有三个按钮的窗口,第一个和最后一个按钮使用同样的刷子。为了重用,我们在资源中为窗口定义了图像刷子。
<Window.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="happyface.jpg" Opacity="0.3">
</ImageBrush>
</Window.Resources>
考虑第一个特性x:Key,这为资源分配了一个名字,用于在资源集合中检索。名字可以使用任何字符串。
引用资源使用标记扩展,有两种:静态资源,动态资源。静态资源在窗口第一次创建时,被设置一次。动态资源每当资源改变时,被应用。这个例子中,图像刷子不会改变,所以使用静态资源。
<Button Background="{StaticResource TileBrush}"
Margin="5" Padding="5" FontWeight="Bold" FontSize="14">
A Tiled Button
</Button>
动态资源具有静态资源的功能,只是多一些开销。
<Button Background="{DynamicResource TileBrush}">
资源的层次结构
每个元素都有自己的资源集合,WPF沿元素树向上执行一个递归的搜索,查找你希望的资源。在当前的例子中,你能移动图像刷子资源从窗口的资源集合到StackPanel的资源集合。应用程序的工作方式不会变化。你也能将图像刷子放在按钮的资源集合,但是每个按钮都要定义一次。
当使用一个静态的资源,必须在引用资源之前定义资源。
结果,如果你在按钮元素中放置资源,需要先定义资源,再设置背景。
<Button Margin="5" Padding="5" FontWeight="Bold" FontSize="14">
<Button.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 10 10"
ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</Button.Resources>
<Button.Background>
<StaticResource ResourceKey="TileBrush"/>
</Button.Background>
<Button.Content>Another Tiled Button</Button.Content>
</Button>
这个例子中,使用嵌套元素引用资源。使用ResourceKey指向正确的资源。
可以在不同的资源集合中使用相同的资源名:
<Window x:Class="Resources.TwoResources"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Resources" Height="300" Width="300" >
<Window.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</Window.Resources>
<StackPanel Margin="5">
<Button Background="{StaticResource TileBrush}" Padding="5"
FontWeight="Bold" FontSize="14" Margin="5" >A Tiled Button</Button>
<Button Padding="5" Margin="5"
FontWeight="Bold" FontSize="14">A Normal Button</Button>
<Button Background="{DynamicResource TileBrush}" Padding="5" Margin="5"
FontWeight="Bold" FontSize="14">
<Button.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="sadface.jpg" Opacity="0.3"></ImageBrush>
</Button.Resources>
<Button.Content>Another Tiled Button</Button.Content>
</Button>
</StackPanel>
</Window>
在这种情况下,按钮使用它发现的第一个元素。因为他从自己开始搜索资源集合,第二个按钮使用sadface.jpg图像,而第一个按钮从容器窗口获得刷子,使用happyface.jpg图像。
静态资源和动态资源
静态资源一次性从资源集合抓取对象。
动态资源每当需要时查找资源集合中的对象。
仅当下列情况使用动态资源:
- 资源依赖于系统设置(如Windows当前颜色和字体)。
- 以编程方式替换资源对象(例如,实现某种动态皮肤特征)。
非共享资源
通常,当在多个地方使用一个资源时,使用的是相同的对象实例。也就是说,资源是共享的。
为关闭共享,使用Shared属性:
<ImageBrush x:Key="TileBrush" x:Shared="False" ...></ImageBrush>
每次引用非共享元素,都会克隆一个新的资源实例。
在代码中访问资源
在代码中使用 FrameworkElement.FindResource()方法引用资源:
private void cmdChange_Click(object sender, RoutedEventArgs e)
{
Button cmd = (Button)sender;
ImageBrush brush = (ImageBrush)sender.FindResource("TileBrush");
...
}
另一个是TryFindResource()方法。
应用程序资源
应用程序范围的资源定义在App.xaml文件中,例如:
<Application x:Class="Resources.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Menu.xaml"
>
<Application.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="happyface.jpg" Opacity="0.3">
</ImageBrush>
</Application.Resources>
</Application>
系统资源
System.Windows名字空间有三个类: SystemColors, SystemFonts, 和SystemParameters。
SystemParameters包裹一个巨大的设置列表。用于描述各种屏幕元素的标准尺寸、键盘和鼠标设置,和屏幕尺寸,和是否打开各种图形效果(如热跟踪,滴落阴影,和拖拽时显示窗口内容)。
这三个类暴露的接口都是静态属性。
以Color结尾的属性用法:
label.Foreground = new SolidBrush(SystemColors.WindowTextColor);
更有效率地,类中也预定义了刷子属性,这些属性以Brush结尾:
label.Foreground = SystemColors.WindowTextBrush;
使用属性扩展设置前景属性:
<Label Foreground="{x:Static SystemColors.WindowTextBrush}">
这个例子没有使用动态资源,这得到的只是系统变量的一个快照,当变量改变时,用户界面不会随之改变。系统类还定义了一族以Key结尾的属性,用于引用动态资源。这是使用动态资源的例子:
<Label Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
动态资源使用的键由SystemColors.WindowTextBrushKey属性定义。因为该属性是静态的,又需要包裹在静态标记扩展中。
资源词典
资源词典用于多个工程间共享资源。详见277页。