【WPF标记扩展】StaticResource 静态资源、DynamicResource 动态资源()

(一)基础知识

静态资源(Static Resource),动态资源(Dynamic Resources)。这两者的区别是:静态资源在第一次编译后即确定其对象或值,之后不能对其进行修改。动态资源则是在运行时决定,当运行过程中真正需要时,才到资源目标中查找其值。因此,我们可以动态地修改它。由于动态资源的运行时才能确定其值,因此效率比静态资源要低。

动态修改,是两种资源最显著的差异,也是极其重要的知识点,下面这个例子就是最好的说明,简单但能解决问题。

<Window x:Class="StaticDynamicResources.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib" 
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>     
        <sys:String x:Key="Content" >
           Hello World!
        </sys:String>
    </Window.Resources>
    <Grid>
        <WrapPanel>
            <TextBlock Text="静态"/>
            <TextBox Text="{StaticResource Content}" Width="100" x:Name="TextBox1"/>
            <TextBlock Text="动态" Margin="10,0,0,0"/>
            <TextBox Text="{DynamicResource Content}" Width="100" x:Name="TextBox2"/>
            <Button Content="改变资源值" Click="ChangeBtn_Click" Width="100"/>       
        </WrapPanel>
    </Grid>
</Window>
private void ChangeBtn_Click(object sender, RoutedEventArgs e)
  {
      this.Resources["Content"] = "内容变了";
  } 

我们点击Button发现,后面的TextBox值可以发生变化,因为它用是的动态资源。有趣的是,将上述程序Window.Resources放在获取资源后面,会出现错误将会收到错误提示:“StaticResource reference ‘content’ was not found.”

出现此问题的原因是:StaticResource 查询行为不支持向后引用,即不能引用在引用点之后才定义的资源。而DynamicResource可以向后引用,即DynamicResource运行时才查找并加载所定义的资源。

(二) 运用场合

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)希望资源可以向前引用时(如上面在WrapPanel中引用content一样) (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)查找系统资源。

非共享资源

正常情况下资源使用的是统一对象实例,这种行为成为共享,如果希望每次都创建一个新的对象可如下设置。x:Shared=”False”

<ImageBrush x:Key="TitleBrush"  x:Shared="False"  TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>

通过代码访问资源

private void txt_TextChanged(object sender, RoutedEventArgs e)
        {
            Button cmd = (Button)sender;
            ImageBrush brush = (ImageBrush)cmd.FindResource("TitleBrush");
        }

可使用TryFindResource()代替FindResource()。如果找不到资源会返回null,而不是抛异常。

 应用程序资源

窗口不是查找应用程序资源的最后一站,如果在控件或其他容器中知道包含窗口或页面找不到指定的资源。WPF会继续查找为应用程序定义的资源。在Visual Studio中,这些资源在App.xaml文件的标记中定义的资源

<Application x:Class="HelloWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="GridDemoWindow.xaml">
    <Application.Resources>
        <ImageBrush x:Key="TitleBrush" TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
    </Application.Resources>
</Application>

应用程序资源为整个应用程序中重用对象提供了一种极佳的方法。

系统资源

当某个元素查找资源时,应用程序资源仍然不是最后一站。如果没有在应用程序资源中找到所需的资源,元素还会继续查找系统资源。

系统资源的三个类

  • SystemColors   访问系统颜色设置。
  • SystemFonts    访问字体设置。
  • SystemParamerers  封装了大量的设置列表,这些设置描述了各种屏幕像素的标准尺寸、键盘和鼠标设置、屏幕尺寸以及各种图形效果(如热跟踪、阴影以及拖动窗口时显示窗口内容)是否已经打开。

StaticResource指向的对象通常是Xaml里定义的Style或者Template之类。 x:static指向的对象通常是后台代码里的某个静态类。用来在Xaml中引用类的静态字段或静态属性,如Text={x:Static local:Window1.ShowText}。 

静态更改:

<Button Background="{x:Static SystemColors.DesktopBrush}" Content="Hello, World!" />

动态更改:

<Button Background="{DynamicResource {x:Static SystemColors.DesktopBrushKey}}" Content="Hello, World!" />

资源字典

如果希望多个项目中共享资源,可创建资源字典。资源字典只是xaml文档,除了存储希望使用的资源外,不做其他任何事情

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ImageBrush x:Key="TitleBrush" TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</ResourceDictionary>

使用资源字典将其整合到应用程序资源中,可添加多个

<Application x:Class="HelloWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="GridDemoWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="AppBrushs.xaml"></ResourceDictionary>
                <ResourceDictionary Source="AppBrushs2.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>  
    </Application.Resources>
</Application>
————————

(一)基础知识

静态资源(Static Resource),动态资源(Dynamic Resources)。这两者的区别是:静态资源在第一次编译后即确定其对象或值,之后不能对其进行修改。动态资源则是在运行时决定,当运行过程中真正需要时,才到资源目标中查找其值。因此,我们可以动态地修改它。由于动态资源的运行时才能确定其值,因此效率比静态资源要低。

动态修改,是两种资源最显著的差异,也是极其重要的知识点,下面这个例子就是最好的说明,简单但能解决问题。

<Window x:Class="StaticDynamicResources.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib" 
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>     
        <sys:String x:Key="Content" >
           Hello World!
        </sys:String>
    </Window.Resources>
    <Grid>
        <WrapPanel>
            <TextBlock Text="静态"/>
            <TextBox Text="{StaticResource Content}" Width="100" x:Name="TextBox1"/>
            <TextBlock Text="动态" Margin="10,0,0,0"/>
            <TextBox Text="{DynamicResource Content}" Width="100" x:Name="TextBox2"/>
            <Button Content="改变资源值" Click="ChangeBtn_Click" Width="100"/>       
        </WrapPanel>
    </Grid>
</Window>
private void ChangeBtn_Click(object sender, RoutedEventArgs e)
  {
      this.Resources["Content"] = "内容变了";
  } 

我们点击Button发现,后面的TextBox值可以发生变化,因为它用是的动态资源。有趣的是,将上述程序Window.Resources放在获取资源后面,会出现错误将会收到错误提示:“StaticResource reference ‘content’ was not found.”

出现此问题的原因是:StaticResource 查询行为不支持向后引用,即不能引用在引用点之后才定义的资源。而DynamicResource可以向后引用,即DynamicResource运行时才查找并加载所定义的资源。

(二) 运用场合

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)希望资源可以向前引用时(如上面在WrapPanel中引用content一样) (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)查找系统资源。

非共享资源

正常情况下资源使用的是统一对象实例,这种行为成为共享,如果希望每次都创建一个新的对象可如下设置。x:Shared=”False”

<ImageBrush x:Key="TitleBrush"  x:Shared="False"  TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>

通过代码访问资源

private void txt_TextChanged(object sender, RoutedEventArgs e)
        {
            Button cmd = (Button)sender;
            ImageBrush brush = (ImageBrush)cmd.FindResource("TitleBrush");
        }

可使用TryFindResource()代替FindResource()。如果找不到资源会返回null,而不是抛异常。

 应用程序资源

窗口不是查找应用程序资源的最后一站,如果在控件或其他容器中知道包含窗口或页面找不到指定的资源。WPF会继续查找为应用程序定义的资源。在Visual Studio中,这些资源在App.xaml文件的标记中定义的资源

<Application x:Class="HelloWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="GridDemoWindow.xaml">
    <Application.Resources>
        <ImageBrush x:Key="TitleBrush" TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
    </Application.Resources>
</Application>

应用程序资源为整个应用程序中重用对象提供了一种极佳的方法。

系统资源

当某个元素查找资源时,应用程序资源仍然不是最后一站。如果没有在应用程序资源中找到所需的资源,元素还会继续查找系统资源。

系统资源的三个类

  • SystemColors   访问系统颜色设置。
  • SystemFonts    访问字体设置。
  • SystemParamerers  封装了大量的设置列表,这些设置描述了各种屏幕像素的标准尺寸、键盘和鼠标设置、屏幕尺寸以及各种图形效果(如热跟踪、阴影以及拖动窗口时显示窗口内容)是否已经打开。

StaticResource指向的对象通常是Xaml里定义的Style或者Template之类。 x:static指向的对象通常是后台代码里的某个静态类。用来在Xaml中引用类的静态字段或静态属性,如Text={x:Static local:Window1.ShowText}。 

静态更改:

<Button Background="{x:Static SystemColors.DesktopBrush}" Content="Hello, World!" />

动态更改:

<Button Background="{DynamicResource {x:Static SystemColors.DesktopBrushKey}}" Content="Hello, World!" />

资源字典

如果希望多个项目中共享资源,可创建资源字典。资源字典只是xaml文档,除了存储希望使用的资源外,不做其他任何事情

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ImageBrush x:Key="TitleBrush" TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</ResourceDictionary>

使用资源字典将其整合到应用程序资源中,可添加多个

<Application x:Class="HelloWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="GridDemoWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="AppBrushs.xaml"></ResourceDictionary>
                <ResourceDictionary Source="AppBrushs2.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>  
    </Application.Resources>
</Application>