之前我的博客文章""中概略性地提到过DynamicResource与StaticResource的区别。其中有这么一句,确切地说是两句:静态资源在第一次编译后即确定其对象或值,之后不能对其进行修改。动态资源则是在运行时决定,当运行过程中真正需要时,才到资源目标中查找其值。
下面用例子更详细地说明DynamicResource与StaticResource的区别。
先看看这段XAML代码:// LinearGradientBrush.xaml<Window x:Class="BrawDraw.Com.LinearGradientBrush.Window1" xmlns="" xmlns:x="" Title="LinearGradientBrush" Height="300" Width="300"> <Canvas Background="{ DynamicResource innerLgbResource}"> <Canvas.Resources> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" x:Key="innerLgbResource"> <GradientStop Color="Yellow" Offset="0.0" /> <GradientStop Color="Orange" Offset="0.5" /> <GradientStop Color="Red" Offset="1" /> </LinearGradientBrush> </Canvas.Resources> </Canvas></Window>注意:innerLgbResource是基于Yellow, Orange, Red三种颜色的渐变。相应的cs文件:// LinearGradientBrush.xaml.csusing System;using System.Windows;namespace BrawDraw.Com.LinearGradientBrush
{ /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } }}运行的效果:图1注意XAML代码中的这句:<Canvas Background="{
DynamicResource innerLgbResource}">,Canvas的背景使用了动态资源。如果你将它改为<Canvas Background="{ StaticResource innerLgbResource}">,将会收到错误提示:“StaticResource reference 'innerLgbResource' was not found.”出现此问题的原因是:StaticResource 查询行为不支持向前引用,即不能引用在引用点之后才定义的资源。而DynamicResource可以向前引用,即DynamicResource运行时才查找并加载所定义的资源。接下来我们来“变变花样”。
先在App.xaml中加入应用程序级资源:<Application x:Class="BrawDraw.Com.LinearGradientBrush.App" xmlns="" xmlns:x="" StartupUri="LinearGradientBrush.xaml"> <Application.Resources> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" x:Key="appLgbResource"> <GradientStop Color="Beige" Offset="0.0" /> <GradientStop Color="Red" Offset="0.3" /> <GradientStop Color="Yellow" Offset="0.5" /> <GradientStop Color="Green" Offset="0.75" /> <GradientStop Color="Orange" Offset="1" /> </LinearGradientBrush> <Style TargetType="Canvas"> <Setter Property="Background" Value="{ StaticResource appLgbResource}"> </Setter> </Style> </Application.Resources></Application>注意<Application.Resources>...</Application.Resources>之间的部分。这里使用了从Beige, Red, Yellow, Green到Orange五种颜色的渐变。同时,将Canvas的背景属性使用Style/Setter的方式设置为这五种给定的渐变色。由于此五种颜色是一次性设置,之后不再改变,所以使用了StaticResource。[讨论] 可以使用DynamicResource吗?比如:<Setter Property="Background" Value="{DynamicResource appLgbResource}">答案是:可以!然后将LinearGradientBrush.xaml中<Canvas Background="{ DynamicResource innerLgbResource}">这句改成:<Canvas Background="{ StaticResource appLgbResource}">,运行结果:图2接着试验:
(1)将<Canvas Background="{ StaticResource appLgbResource}">改成:<Canvas Background="{ DynamicResource appLgbResource}">试试,效果与图2一样!(2)将<Canvas Background="{...}">中Background属性去掉,改成:<Canvas>,运行效果也与图2一致。(3)如果改成:<Canvas Background="{ DynamicResource innerLgbResource}">时,则显示图1所示基于基于Yellow, Orange, Red三种颜色的渐变效果。(4)你甚至可以这样:在App.xml中使用<Setter Property="Background" Value="{ DynamicResource appLgbResource}">,而在LinearGradientBrush.xaml中使用<Canvas Background="{ StaticResource appLgbResource}">(运行效果如图2)。探讨:
1、当引用资源时,选择StaticResource还是DynamicResource的考虑因素:(1)在哪里创建资源?(资源的范围或层级)a. 资源是在一个Page/Canvas/Window中?b. 在应用程序范围中?c. 在松散的Xaml中?d. 在某个特定的Object(比如某个特定的Button)中?物件级:此时,资源只能套用在这个Object物件,或套用至该物件的子物件。 文件级:如果将资源定义在Window或Page层级的XAML档中,那么可以套用到这个文件中的所有物件。 应用程序级:如果我们将资源定义在App.xaml 中,那么,就可以将资源套用到应用程序内的任何地方。 字典级:当我们把资源封装成一个资源字典, 定义到一个ResourceDictionary的XAML文件时,就可以在另一个应用程序中重复使用。(2) 应用程序的功能:是否在运行时改变资源?
如果需要改变,则使用DynamicResource。(3) 每个资源引用类型不同的寻找行为。(需要支持向前引用吗?)StaticResources的适用场合:(1)在资源第一次引用之后无需再修改资源的值。(2)资源引用不会基于运行时的行为进行重新计算,比如在重新加载Page/Window的时候。(3)当需要设置的属性不是DependencyObject或Freezable类型的时候,用StaticResource。(4)当需要将资源编译到dll中,并打包为程序的一部份,或者希望在各应用程序之间共享时,也使用StaticResource。(5)当需要为一个自定义控件创建一个Theme,并Theme中使用资源,就需要使用StaticResource。因为StaticResource的资源查找行为时可预测的,并且本身包含在Theme中。而对于DynamicResource,即使资源是定义在Theme中,也只能等到运行时确定,导致一些可能意料不到的情况发生。(6)当需要使用资源设置大量的依赖属性(Dependency Property)的时候。由于依赖属性具有属性系统提供的值缓存机制,所以,如果能在程序装载时设置依赖属性的值,这样,依赖属性就不需要检查自己的值并返回最后的有效值了。 Dynamic Resource一般使用在如下场合:(1)资源的值依赖一些条件,而该条件直到运行时才能确定。包括系统资源,或是用户可设置的资源。比如:可以创建引用系统属性诸如SystemColors,SystemFonts来设置值,而这些属性是动态的,它们的值又来自于运行环境和操作系统。(2)为自定义控件引用或创建Theme Style。(3)希望在程序运行期间调整资源字典的内容时。(4)希望资源可以向前引用时(如上面在Canvas中引用innerLgbResource一样)(5)资源文件很大,希望在运行时才加载。(6)要创建的Style的值可能来自于其它值,而这些值又依赖于Theme或用户的设置。(7)当引用资源的元素的父元素有可能在运行期改变,这个时候也需要使用动态资源。因为父元素的改变将导致资源查询的范围。Dynamic resource的限制条件:属性必须是依赖属性,或是Freezable的。资源的查询方式Static Resource的查询(1)查找使用该资源的元素的Resource字典;(2)顺着逻辑树向上查找父元素的资源字典,直到根节点;(3)查找Application资源;(4)不支持向前引用,即:不能引用在引用点之后才定义的资源。Dynamic Resource的查询(1)查找使用该资源的元素的Resource字典;如果元素定义了一个Style 属性,将查找Style中的资源字典;如果元素定义了一个Template属性,将查找FrameworkTemplate中的资源字典。(2)顺逻辑树向上查找父元素的资源字典,直到根节点;(3)查找Application资源;(4)查找当前激活状态下的Theme资源字典;(5)查找系统资源。