WPF程序设计指南:Resource
注:一下内容及代码基本来自Charles Petzold著,蔡学庸译,电子工业出版社出版的《Windows Presentation Foundation 程序设计指南》一书
1. 概述
- 就像为了弥补XML没有循环语句而使用了Style一样,Resource是为了弥补XML没有静态只读字段而设计的。
- 和静态只读字段一样,资源对象在运行时只被建立一次,而且被引用他们的element共享
- 所有资源存储在一个ResourceDictionary类型的对象中, 该对象中每个项目都具有一个key,用来识别该对象
- FrameworkElement、FrameworkContentElement、Appliction都定义了一个名为Resources的property,类型为ResourceDictionary
2. 使用格式
例如在一个StackPanel中,定义Recources:
<StackPanel>
<StackPanel.Resources>
...
</StackPanel.Resources>
...
</StackPanel>
而在Resources section内,每个资源定义具有以下格式:
<SomeType x:Key="mykey" ...>
...
</SomeType>
在使用的时候,可以用attribute语法或者property element语法,来设定具体的property
3. 使用实例
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<StackPanel.Resources>
<s:Double x:Key="fontsizeLarge">
18.7
</s:Double>
<s:Double x:Key="fontsizeSmall">
14.7
</s:Double>
</StackPanel.Resources>
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24">
<Button.FontSize>
<StaticResource ResourceKey="fontsizeLarge" />
</Button.FontSize>
Button with large FontSize
</Button>
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24"
FontSize="{StaticResource fontsizeSmall}" >
Button with small FontSize
</Button>
</StackPanel>
- 代码说明:
1. 第一个Button使用property element语法,获取FrontSize资源,使用一个StaticResource的element和一个ResourceKey的attribute,来表示此项目的key
2. 第二个Button使用attribute语法,FrontSize attribute被设定为一个字符串,将“StaticResource”和Key的名字放在大括号内。
- Resources section总是定义在一个element的最顶端,因为任何资源都必须在文件中被引用之前定义。
4. 具有相同key的Resource
- 在特定的Resources Collection内,key不能重复,但是相同的key可以出现在两个Resource collection内。
- 当一个资源必须被定位时,会先从element所引用的TResources collection开始查找,然后沿着这个树状结构往上找,直到找到key为止。如:
代码<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Orientation="Horizontal">
<StackPanel.Resources>
<SolidColorBrush x:Key="brushText" Color="Blue" />
</StackPanel.Resources>
<StackPanel>
<StackPanel.Resources>
<SolidColorBrush x:Key="brushText" Color="Red" />
</StackPanel.Resources>
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24"
Foreground="{StaticResource brushText}">
Button with Red text
</Button>
</StackPanel>
<StackPanel>
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24"
Foreground="{StaticResource brushText}">
Button with Blue text
</Button>
</StackPanel>
</StackPanel>
代码说明:
第一个Button使用红色字体,第二个使用蓝色字体(父元素的资源)
5. 将一个element或者控件定义为资源
- 可以将一个element或者控件定义为资源,例如:
<Button x:Key="btn"
FontSize="24">
Resource Button
</Button>
然后使用它:<StaticResource ResourceKey="btn" />但是只能这样使用一次,因为只有一个Button对象可用。
6. 在C#代码中加入一个resource
stack.Resources.Add("brushText", new SolidColorBrush(Colors.Blue));
- Resources的类型是ResourceDictionary,ResourceDictionary定义有一个Add方法,第一个参数是key,第二个参数是object。
- 使用的时候,用FindResource方法来找出特定的key(该方法同样具备找祖先的resource的功能)
7. 使用x:Static读取静态属性或字段
- 如果想要将一个Button的Content property设定为SomeClass类的静态property,名为SomeStaticProp,语法为:
Content="{x:Static SomeClass:SomeStaticProp}"
或者在property element中使用一个x:Static element:
<Button.Content>
<x:Static Member="SomeClass:SomeStaticProp" />
</Button.Content>
- 也可以在C#代码中定义静态属性或者字段,然后在XML文件中读取。如有C#代码:
代码using System;
using System.Windows;
using System.Windows.Media;
namespace Petzold.AccessStaticFields
{
public static class Constants
{
// Public static members.
public static readonly FontFamily fntfam =
new FontFamily("Times New Roman Italic");
public static double FontSize
{
get { return 72 / 0.75; }
}
public static readonly LinearGradientBrush brush =
new LinearGradientBrush(Colors.LightGray, Colors.DarkGray,
new Point(0, 0), new Point(1, 1));
}
}在XMAL中读取以上的静态只读属性和字段,注意要引用命名空间
代码<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:Petzold.AccessStaticFields"
x:Class="Petzold.AccessStaticFields.AccessStaticFields"
Title="Access Static Fields"
SizeToContent="WidthAndHeight">
<TextBlock Background="{x:Static src:Constants.brush}"
FontSize="{x:Static src:Constants.FontSize}"
TextAlignment="Center">
<TextBlock.FontFamily>
<x:Static Member="src:Constants.fntfam" />
</TextBlock.FontFamily>
Properties from<LineBreak />Static Fields
</TextBlock>
</Window>
8. 关于使用系统的静态属性
- SystemColors、SystemParameters以及SystemFonts类都具有一大群的property,XMAL文件可以用x:Static读取他们
- 这些静态property都是成对出现的,有一个名为Whatever的property,就有一个名为WhatereverKey的property,所以以"Key"结尾的property都返回一个ResourceKey类型的对象
- 例如
{x:Static SystemColors.ActiveCaptionBrush}{x:Static SystemColors.ActiveCaptionBrushKey}
返回一个ResourceKey类型的对象。因此:
Foreground="{StaticResource {x:Static SystemColors.ActiveCaptionBrushKey}}"等于与:
Foreground="{x:Static SystemColors.ActiveCaptionBrush}"而以下的用法是错误的:
Foreground="{StaticResource SystemColors.ActiveCaptionBrushKey}"
9. 动态资源:DynamicResource
- 如果需要element的前景色随着系统的改变而改变,可以使用以下设置:
Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"
完整的语法是:
<Label ... >
<Label.Foreground>
<DynamicResource>
<DynamicResource.ResourceKey>
<x:Static Member="SystemColors.ActiveCaptionBrushKey" />
</DynamicResource.ResourceKey>
</DynamicResource>
</Label.Foreground>
</Label>
- 如果使用StaticResource,key被用来存取对象一次,然后对象会被保留;如果使用DynamicResource,此key会被保留,而对象需要的时候会被取用。
- DynamicResource主要用来存取系统资源,比如系统颜色。但是如果要在element或控件上的资源取得相应的效果,需要用绑定。
10. 在定义静态资源时使用动态资源
当你建立成为资源的画刷的时候,也可以使用系统的颜色:
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="{DynamicResource
{x:Static SystemColors.InactiveCaptionBrushKey}}">
<StackPanel.Resources>
<LinearGradientBrush x:Key="dynabrush1"
StartPoint="0 0" EndPoint="1 1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0"
Color="{DynamicResource
{x:Static SystemColors.ActiveCaptionColorKey}}" />
<GradientStop Offset="1"
Color="{DynamicResource
{x:Static SystemColors.InactiveCaptionColorKey}}" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="dynabrush2"
Color="{DynamicResource
{x:Static SystemColors.ActiveCaptionColorKey}}" />
</StackPanel.Resources>
<Label HorizontalAlignment="Center"
FontSize="96"
Content="Dynamic Resources"
Background="{StaticResource dynabrush1}"
Foreground="{StaticResource dynabrush2}" />
</StackPanel>
代码说明:
1. 当系统颜色发生改变的时候,label的前景色和背景色会跟着发生改变,但是两个作为静态资源的Brush并没有被替代,而只是color发生了改变。
2. 如果将label的Background和Foreground改成DynamicResource,此程序不会响应系统颜色的改变。因为当DynamicResource希望重新建立一个被key所引用的对象,而此画刷对象并没有被重新建立
11. 使用与动态资源相同的key覆盖动态资源
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Orientation="Horizontal">
<StackPanel>
<StackPanel.Resources>
<SolidColorBrush
x:Key="{x:Static SystemColors.ActiveCaptionBrushKey}"
Color="Red" />
</StackPanel.Resources>
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24"
Foreground="{DynamicResource
{x:Static SystemColors.ActiveCaptionBrushKey}}">
Button with Red text
</Button>
</StackPanel>
<StackPanel>
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24"
Foreground="{DynamicResource
{x:Static SystemColors.ActiveCaptionBrushKey}}">
Button with Blue text
</Button>
</StackPanel>
</StackPanel>
代码说明:
1. 第一个Button使用的是已经被覆盖了的资源,前景色为红色
2. 第二个Button使用的是没有被覆盖的动态资源,反应的是系统的颜色
12. 合并资源
如果有一个定义了resource dictionary的XAML文件 :MyResources1.xaml
另外有一个定义了resource dictionary的XAML文件 :MyResources2.xaml
现在有一个工程,想要使用这两个文件所定义的资源,你可以让这两个文件成为此项目的一部分,将“Build Action”设定为“Page”或者“Resource”,并在App.xmal文件中:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="UseCommonResourcesWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyResources1.xaml" />
<ResourceDictionary Source="MyResources2.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
如果这些文件中拥有多个相同的key,那么先出现的资源会被后出现的资源替代。