• WPF,Silverlight与XAML读书笔记第十七 资源之逻辑资源


    说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。

     

        逻辑资源是相对于二进制文件而言,其定义于XAML,但同样可在过程代码中创建与使用逻辑资源。

        本质上逻辑资源是存储于元素的Resource属性(System.Windows.ResourceDictionary类型)的.NET对象。这个属性定义于FrameworkElement与FrameworkContentElement,所以大部分WPF元素中都可以找到这个属性。逻辑资源通常是样式或数据提供程序。

    在介绍逻辑资源的使用方法与优势之前,首先回顾下ASP.NET模型中样式的定义方式。在ASP.NET中,我们将服务器控件的样式定义于一个Skin文件中,并需要设置这个样式的控件中通过SinkID引用这个样式。

    WPF逻辑资源的工作方式类似,其将一系列可以被应用于多个元素的资源(如样式)保存于这些元素的容器,如Window的Resource属性,或者如果逻辑资源将在整个应用程序中可用时,可以将其保存于Application的Resource属性。先通过一个示例纵览一下逻辑资源(以样式为例)的设置与使用。

    XAML:

     1 <Window.Resources> 
     2 <SolidColorBrush x:Key="bgBrush">Yellow</SolidColorBrush> 
     3 <SolidColorBrush x:Key="btnBgBrush">Green</SolidColorBrush> 
     4 <SolidColorBrush x:Key="borderBrush">Red</SolidColorBrush> 
     5 </Window.Resources> 
     6 <Window.Background> 
     7 <StaticResource ResourceKey="bgBrush"/> 
     8 </Window.Background> 
     9 <Grid> 
    10 <Button Background="{StaticResource btnBgBrush}" BorderBrush="{StaticResource borderBrush}" Width=" 66" Height="36" > 
    11 按钮!
    12 </Button> 
    13 </Grid>

    代码中斜体为定义逻辑资源(样式)的语法,粗体为使用逻辑资源(样式)的语法。效果图如下:

    上述代码中,你可以看出应用逻辑资源需要使用StaticResource这个标记扩展(System.Window.StaticResource)。使用标记扩展有两种方式:

    1. 可以将扩展标记作为属性元素(如代码中Window的Background的设置)。
    2. 可以使用定位参数方式的标记扩展语法来设置,如Button的Background与BorderBrush的设置。

    使用资源的一个好处是,可以在任意时刻统一替换掉资源,如将上述例子中的bgBrush重新定义如下:

    1 <LinearGradientBrush x:Key="bgBrush" StartPoint="0,0" EndPoint="1,1"> 
    2 <GradientStop Color="Blue" Offset="0"/> 
    3 <GradientStop Color="White" Offset="0.5"/> 
    4 <GradientStop Color="Red" Offset="1"/> 
    5 </LinearGradientBrush>

    新的效果如下:

    资源查找:

        StaticResource标记扩展接受的参数是资源字典(ResourceDictionary的对象),这个资源字典可以是位于当前元素,也可以是位于父元素,甚至是应用程序级的Resource属性中。

        StaticResource标记扩展类,可以向上遍历逻辑树查找资源,当找不到时会抛出InvalidOperationException异常。另外每个独立的ResourceDictionary中的键名不能重复,但多个不同的ResourceDictionary中的键名可以相同,由于采用向上遍历的策略,同名资源中最近的将被采用。

    静态资源与动态资源

        静态访问资源的方式使用前文所示的StaticResource这个标记扩展来提供。这种资源仅在第一次需要加载资源时加载。

        动态加载资源的方式由DynamicResource这个标记扩展来实现,这个标记扩展与StaticResource在使用方式上(包括其遍历元素树的方式上)都相同。最大的不同是DynamicResource会跟踪资源的变换,并在资源变化后重新加载资源。

        可以将动态资源与静态资源结合使用,即给一个资源固定一个名称,如果需要使用静态方式引用就使用StaticResource调用,反之就使用DynamicResource来调用这个资源对象。

    下面具体分析下两者的区别:

    最主要的区别就是,动态资源可以在资源变化后自动更新,而使用静态资源时,如果资源发生变化则需要手工编码来更新。而这就导致了一些的不同。

        首先由于动态资源需要跟踪变化,动态资源需要占用更多的资源。

    另一方面动态资源可以改善加载时间,因为这种引用到实际使用时才会发生。而对静态资源的引用总是发生在Window或Page加载之后。

    另外,动态资源只能用于设置依赖属性,而静态资源可以用于任何地方(不单是设置属性)。例如:静态资源可以当作元素来使用(见下面示例)

    1 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    2  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    3 <Image Height="20" Width="36" Source="Logo.png" /> 
    4 </Window>

    如果将以上代码用静态资源的方式实现代码如下:

    1 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    2  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    3 <Window.Resources> 
    4 <Image x:Key="logo" Source="Logo.png" /> 
    5 </Window.Resources> 
    6 <StaticResource ResourceKey="logo" /> 
    7 </Window>

    如上代码中的粗体部分就是作为元素的静态资源。

    注意:不能把一个对象用于多个资源中。

        静态资源访问不同于动态资源访问的另一个方面是StaticResource不支持向前引用,也就是说任何资源必须在XAML中声明后才可以使用。所以,当资源与资源访问定义于同一个元素中时,不能使用StaticResource这个标记扩展来引用资源。其原因是当这两者共存于一个元素中时,作为属性元素方式定义的资源需要放在后面,(而StaticResource这个标记会在前面),见下面这段代码(其是错误的!错误提示即为:未找到 StaticResource 引用"bgBtn"):

    1 <Button x:Name="Test" Background="{StaticResource bgBtn}"> 
    2 <Button.Resources> 
    3 <SolidColorBrush x:Key="bgBtn">Yellow</SolidColorBrush> 
    4 </Button.Resources> 
    5 </Button>

    而DynamicResource这个标记扩展不存在这个问题,所以正确的代码如下(注意粗体部分):

    1 <Button x:Name="Test" Background="{DynamicResource bgBtn}"> 
    2 <Button.Resources> 
    3 <SolidColorBrush x:Key="bgBtn">Yellow</SolidColorBrush> 
    4 </Button.Resources> 
    5 </Button>

    资源XAML文件的组合

        逻辑资源可以很方便的嵌入普通的XAML文件中,但是为了更好地组织与管理逻辑资源,可以将它们组织到各个独立的XAML文件中。这样就需要通过一定的方式在一般XAML中引用这些资源XAML 文件,方法就是使用ResourceDictionary类的MergedDictionaries属性来完成。下列代码很好的阐述了这个问题:

    1 <Window.Resources> 
    2 <ResourceDictionary> 
    3 <ResourceDictionary.MergedDictionaries> 
    4 <ResourceDictionary Source="file1.xaml" /> 
    5 <ResourceDictionary Source="file2.xaml" /> 
    6 </ResourceDictionary.MergedDictionaries> 
    7 </ResourceDictionary> 
    8 </Window.Resources>

    这样就可以将file1.xaml与file2.xaml文件中的资源引用添加到当前xaml文件中。如果当这两个文件中资源的key重复时,后添加的资源会覆盖先添加的资源。

    而这个单独的file1.xaml文件格式也有要求,其根元素必须为ResourceDictionary,一个模板可以参见下方:

    1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    2  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    3 <Image x:Key="logo" Source="logo.jpg" /> 
    4 </ResourceDictionary>

    另一种将XAML放入多个文件的方式是使用自定义控件

    资源的共享

        当一个资源被应用到多个地方时,默认使用这个资源对象的同一个实例。将ResourceDictionary中项的x:Shared属性设置为"False"时,在每个使用这个资源的地方都会使用一个不同的资源对象实例,而且可以独立修改每个资源对象。

    使用程序代码定义与应用资源

        前文所述的资源的定义与使用都是使用XAML来完成的。下面来说明一下如何使用C#代码完成同样的工作。

    使用C#定义资源:

    1 mainwindow.Resources.Add("bgBrush", new SolidColorBrush(Colors.Yellow)); 
    2 mainwindow.Resources.Add("borderBrush", new SolidColorBrush(Colors.Red));

    而对于资源的使用,C#代码就与XAML有较大不同,因为XAML使用了标记扩展而C#没有这种特性。

    对于静态资源访问方式,需要通过调用相应元素(需要是从FrameworkElement或FrameworkContentElement继承而来的,因为下面介绍的方法继承自这些基类)的FindResource方法的返回值来取得资源。所以对于下面的XAML代码:

    1 <Button Background="{StaticResource bgBrush}" BorderBrush="{StaticResource borderBrush}" />

    等价的C#代码为:

    1 Button button = new Button(); 
    2 button.Background = (Brush)button.FindResource("bgBrush"); 
    3 button.BorderBrush = (Brush)button.FindResource("borderBrush");

    在FindResource找不到资源时会抛出一个异常,另外有一个TryFindResource方法,在找不到资源时将返回null而不是抛出异常。

    对于动态资源访问方式,需要通过相应元素(需要是从FrameworkElement或FrameworkContentElement继承而来的,因为下面介绍的方法继承自这些基类)的SetResourceReference方法来设置资源到相应元素的属性。对于下面的XAML代码:

    1 <Button Background="{DynamicResource bgBrush}" BorderBrush="{DynamicResource borderBrush}" />

    同样的等价C#代码:

    1 Button button = new Button(); 
    2 button.SetResourceReference(Button.BackgroundProperty, "bgBrush"); 
    3 button.SetResourceReference(Button.BorderBrushProperty, "borderBrush");

    前面介绍的StaticResource不能向前引用的规则在程序代码里同样适用。即把资源添加到一个合适的资源字典(ResourceDictionary)之前,调用FindResource或TryFindResource会失败。而调用SetResourceReference可以正常执行。

    提示:在代码中直接访问资源

    由于资源是一个字典类的集合,所以也可以直接使用字典的方式来访问资源对象。如下代码示例:

    1 Button button = new Button(); 
    2 button.Background = (Brush)mainwindow.Resources["bgBrush"]; 
    3 button.BorderBrush = (Brush)mainwindow.Resources["borderBrush"];

    这种直接访问资源字典的方式,可以在一定程度上提高程序的性能,虽然可能会带来一定的负面影响。

    访问另一个程序集的逻辑资源

        在上篇文章中我们知道可以访问另一个程序集中的二进制资源,同样也可以访问另一个程序集中的逻辑资源。访问的方式是通过名为ComponentResourceKey的标记扩展。要使用ComponentResourceKey,每个资源都必须有一个键名,这个键名是ComponentResourceKey的实例。下面给出一个示例:

    以下代码是定义于程序集A的资源:

    1 <SolidColorBrush 
    2  x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:MyClass}, 
    3  ResourceId=MyClassBrush}">Yellow</SolidColorBrush>

    以下是在程序集B中使用这个资源:

    1 <Button Background="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=otherAssembly:MyClass, ResourceId=MyClassBrush}}" />

        往往在定义资源的程序集中(上例中为程序集A),会通过一个属性将ComponentResourceKey暴露给使用者(即程序集B)。继续上面的例子:

    程序集A中提供的属性:

    1 public object MyClassBrushKey 
    2 {
    3 get { return new ComponentResourceKey(this.GetType(), "MyClassBrush"); } 
    4 }

    这样在程序集B中可以这样使用:

    1 <Button Background="{DynamicResource {x:Static otherAssembly:MyClass.MyClassBrushKey}}" />
     

    访问系统资源

    System.Windows命名空间中有3个封装了系统设置的类:SystemColors、SystemFonts和SystemParameters。这三个类非常适合使用DynamicResource来引用,因为用户可以非常方便的通过更改控制面板来影响运行中的程序。使用动态资源,程序就可以随之发生变化。使用这个系统资源有三种方式:

    XAML:

    1 <Button Background="{x:Static SystemColors.WindowBrush}" />

    C#:

    1 Button b = new Button(); 
    2 b.Background = SystemColors.WindowBrush;

    XAML:

    1 <Button Background="{StaticResource {x:Static SystemColors.WindowBrush}}" />

    C#:

    1 Button b = new Button(); 
    2 b.Background = (Brush)FindResource(SystemColors.WindowBrush);

    XAML:

    1 <Button Background="{DynamicResource {x:Static SystemColors.WindowBrush}}" />

    C#:

    1 Button b = new Button();
    2 SetResourceReference(Button.BackgroundProperty, SystemColors.WindowBrush);

    其中最后一种动态引用方式程序可以随系统设置变化而改变,前两种不会改变。

    本文完

    参考:

    《WPF揭秘》

  • 相关阅读:
    Mac电脑PHP环境-XAMPP:报错 Unable to load dynamic library的解决方法
    Mac下安装JDK(Day 12.05)
    Mac下配置Java Web开发环境
    StiReport使用
    深入浅出图解【计算机网络】 之 【TCP可靠传输的实现2: 超时重传+拥塞控制】
    深入浅出图解【计算机网络】 之 【TCP可靠传输的实现: 三次握手+滑动窗口】
    深入浅出图解【计算机网络】 之 【路由选择协议】
    由Leetcode详解算法 之 动态规划(DP)
    自定义装点博客的“门面”
    数据结构 之 常见的几种“排序”
  • 原文地址:https://www.cnblogs.com/lsxqw2004/p/4616738.html
Copyright © 2020-2023  润新知