WPF程序设计指南:Resource

时间:2021-10-07 21:40:00

注:一下内容及代码基本来自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. 使用实例

WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
   
   
   
< 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为止。如:
    WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
         
         
         
    < 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#代码:
    WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
         
         
         
    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中读取以上的静态只读属性和字段,注意要引用命名空间 

    WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
         
         
         
    < 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}
    返回一个SolidBrush类型的对象,而
        
        
        
    {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}}"

完整的语法是:

WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
   
   
   
< Label ... >
< Label.Foreground >
< DynamicResource >
< DynamicResource.ResourceKey >
< x:Static Member ="SystemColors.ActiveCaptionBrushKey" />
</ DynamicResource.ResourceKey >
</ DynamicResource >
</ Label.Foreground >
</ Label >
  • 如果使用StaticResource,key被用来存取对象一次,然后对象会被保留;如果使用DynamicResource,此key会被保留,而对象需要的时候会被取用。
  • DynamicResource主要用来存取系统资源,比如系统颜色。但是如果要在element或控件上的资源取得相应的效果,需要用绑定。

10. 在定义静态资源时使用动态资源

当你建立成为资源的画刷的时候,也可以使用系统的颜色:

WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
   
   
   
< 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覆盖动态资源

WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
   
   
   
< 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文件中:

WPF程序设计指南:ResourceWPF程序设计指南:Resource代码
   
   
   
< 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,那么先出现的资源会被后出现的资源替代。