【笔记】【WPF编程宝典】 第10章 资源


  WPF资源系统是一种保管一系列有用对象的简单方法,从而可以更容易地重用这些对象。尽管可在代码中创建和操作资源,但通常在XAML标记中定义资源。一旦定义资源,就可以在窗口标记的所有其他部分使用资源。

10.1 资源基础

  WPF允许在代码中以及在标记中的各个位置定义资源。资源具有以下优点:

  • 高效。可以通过资源定义对象,并在标记中的多个地方使用。这会精简代码,使其更高搞笑。
  • 可维护性。可通过资源使用低级的格式化袭击,并将它们移到便于对齐及逆行修改的中央位置。在XAML中创建资源相当于在代码中创建常量。
  • 适应性。一旦特定信息与应用程序的其他部分分离开来,并放置到资源部分中,就可以动态地修改这些信息。

10.1.1 资源集合

  每个元素都有Resources属性,该属性存储了一个资源字典集合(它是ResourceDictionary类的实例)。尽管每个元素都提供了Resources属性(该属性作为FrameworkElement类的一部分定义),但通常在窗口级别定义资源。这是因为每个元素都可以访问各自资源集合中的资源,也可访问所有父元素的资源集合中的资源。
  下面的标记显示了如何定义与使用画刷:

<Window x:Class="Resources.WindowResource"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Resources" Height="300" Width="300">
    <Window.Resources>
        <ImageBrush x:Key="TileBrush" TileMode="Tile"
                    ViewportUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3">
        </ImageBrush>
    </Window.Resources>
    <StackPanel Margin="5">
        <Button Background="{StaticResource TileBrush}" Padding="5" FontWeight="Bold" FontSize="14" Margin="5">A Tiled Button</Button>
        <Button Padding="5" Margin="5" FontWeight="Bold" FontSize="14">A Normal Button</Button>
        <Button Background="{StaticResource TileBrush}" Padding="5" Margin="5" FontWeight="Bold" FontSize="14">Another Tiled Button</Button>
    </StackPanel>
</Window>

  定义画刷资源时,图像画刷的细节不是非常重要。重要的是Key特性(以名称空间前缀x:开头,这会将该画刷放置到XAML名称空间中,而不是WPF名称空间中)。该特性制定了在Window.Resoures.Collection集合中编写画刷索引的名称。当需要检索资源时,只要使用相同名称,就可以使用需要的任何内容。
  为使用XAML标记中的资源,需要一种引用资源的方法。这是通过标记扩展完成的。实际上有两个标记扩展可工使用:一个用于动态资源,另一个用于静态资源。静态资源在首次创建窗口时一次性地设置完毕。而对于动态资源,如果发生了改变,就会重新应用资源。

10.1.2 资源的层次

  每个元素都有自己的资源集合,为了找到期望的资源,WPF在元素树中进行递归搜索。在当前示例中,可将图像画刷从窗口的资源集合移到包含这三个按钮StackPanel面板的资源集合中,而不必改变应用程序的工作方式。也可将图像画刷放到Button.Resources集合中,不过需要定义画刷两次——为每个按钮分别定义一次。
  需要考虑另外一个问题是,当使用静态资源是,必须总是在引用资源之前在标记中定义资源。这意味着尽管从标记角度看,将Window.Resources部分放在窗口的主要内容(包含所有按钮的StackPanel面板)之后是完全合法的,但这回破坏当前示例。当XAML解析器遇到它不知道的资源的静态引用时,会抛出异常。

10.1.3

  上面的示例使用了静态资源(在该例中是图形画刷),所以可能认为对于资源的任何改变都不会有什么反应。然而,事实并非如此。例如,设想在应用了资源并且显示了窗口子厚,执行下面的代码:

ImageBrush brush = (ImageBrush)this.Resource["TileBrush"];
brush.ViewPort = new Rect(0, 0, 5, 5);

  上面的代码从Window.Resources集合中检索画刷,并对它进行操作。当运行此代码时,可能不希望用户界面有任何反应——毕竟它是静态资源。但这一变化会传播给两个按钮。实际上,会使用新设置的ViewPort属性进行更新,而不管是通过静态资源还是动态资源使用画刷。
  这是因为Brush类集成自Freezable类。Freezable类有一个基本的变化跟踪特性。这意味着,无论何时在WPF中改变画刷,所有使用该画刷的控件都会自动更新。
  现在,可能想弄清楚静态资源和动态资源之间到底有什么区别。区别在于静态资源只从资源集合中获取对象一次。根据对象的类型以及使用对象的方式,对象的任何变化都可能被立即注意到。然而,动态资源再每次需要对象时都会重新从集合中查找对象。这意味着可再同一键下放置一个全新对象,而且动态资源会应用该变化。
  作为一般规则,只有再下列情况才需要使用动态属性:

  • 资源具有依赖于系统设置的属性
  • 准备通过编程方式替换资源对象
      然而,不应过度使用动态资源。主要问题是对资源的修改未必会触发对用户界面的更新。

10.1.4 非共享资源

  通常,再多个地方使用吗欧中资源时,使用的时同一个对象实例。这种行为——称为共享——通常这也正是所希望的。然而,也可能希望告诉解析器再每次使用时创建单独的对象实例。为了关闭共享行为,需要使用Shared特性,如下所示:

<ImageBrush x:key="TilBrush" x:Shared="False" .../>

10.1.5 通过代码访问资源

  可使用FrameworkElement.FindResource()方法查找资源。下面是一个实例,当引发Click事件时,会查找按钮资源。

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

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

10.1.6 应用程序资源

  窗口不是查找资源的最后一站。如果再控件或其他容器中找不到指定的资源,WPF会继续检查为应用程序定义的资源集合。在VS中,这些资源是在App.xaml文件中的标记中定义的资源,如下所示:

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

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

10.1.7 系统资源

  常使用的系统资源主要包含SystemColors、SystemFonts和SystemParameters,这些类都位于System.Windows名称空间中。

  • SystemColors类用于访问颜色设置
  • SystemFonts类用于访问字体设置
  • SystemParameters类封装了大量的设置列表,这些设置描述了更重屏幕元素的标准尺寸、键盘和鼠标设置、屏幕尺寸以及各种图形效果是否已经打开。
      在WPF中,可使用静态标记扩展访问静态属性。例如,下面的标记演示了如何使用XAML设置同一标签的前景设:
<Label Foreground="{x:Static SystemColors.WindowTextBrush}">Ordinary text</Lable>

  上面的实例没有使用资源,这可能引发一个小问题——当解析窗口并创建标签时,会根据当前窗口文本颜色的“快照”创建会刷。如果在应用程序运行时,该变了Windows颜色,Label空间不会更新自身。
  为解决这个问题,不能将Foreground属性直接设置为画刷对象,而是需要将它设置为封装了该系统资源的DymamicResource对象。幸运的是,所有SystemXxx类都提供了可返回ResourceKey对象应用的补充属性集,使用这些引用可从系统资源集合中提取资源。这些属性与直接返回对象的普通属性同名,后面加上单词Key。

<Label Foreground="{DymamicResource {x:Static SystemColors.WindowTextBrushKey}}">Ordinary text</Lable>

10.2 资源字典

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

10.2.1 创建资源字典

  下面是一个资源字典示例,它包含一个资源:

<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"
        		ViewportUnits="Absolute" 
        		Viewport="0 0 32 32"
        		ImageSource="happyface.jpg" 
        		Opacity="0.3">
    </ImageBrush>
</ResourceDictionary>

  当为应用程序添加资源字典时,务必将Build Action设置为Page。这样可保证了获得最佳性能而将资源字典编译为BAML。不过,将资源字典的Build Action设置为Resource也是非常完美的,这样它会被嵌入到程序集中,但是不会被编译。当然,在运行时解析它的速度要稍慢一些。

10.2.2 使用资源字典

  为了使用资源字典,需要将其合并到应用程序某些位置的资源集合中。例如,可在特定窗口中执行此操作,但通常将其合并到应用程序的资源集合中,如下所示:

<Application x:Class="Resources.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Menu.xaml">
    <Application.Resources>
        <ResourceDictionary>
        	<ResourceDictionary.MegerdDictionaries>
        		<ResourceDictionary Source="AppBrushes.xaml"/>
        		<ResourceDictionary Source="WinzardBrushes.xaml"/>
        	</ResourceDictionary.MegerdDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

10.2.3 在程序集之间共享资源

  当共享包含一个或多个资源自带你的编译过的程序集时,还需要面对另一种挑战——需要有一种方法来提取所希望的资源并在应用程序种使用资源。为此,可使用两种方法。最直观的解决方法是使用代码创建合适的ResourceDictionary对象。例如,如果类库程序集中有名为ReuseableDictionary.xaml的资源字典,那么可使用下面的代码手动创建该资源字典:

ResourceDictionary resourceDictionary = new ResourceDictionary();
resourceDictionary.Source = new Uri("ResourceLibrary;componet/ReuseableDictionary.xaml", UriKind.Relative);

cmd.Background = (Brush)resourceDictionary["TileBrush"];

  然而,不必手动指定资源。当加载新的资源字典时,窗口中的所有DynamicResource引用都会被自动重新评估。
  如果不想编写任何代码,还有另外一种选择。可使用ComponentResourceKey标记扩展,该编辑扩展时专门针对这种情况而设计的。使用ComponentResourceKey为资源创建键名。通过执行这一步骤,告知WPF准备在程序集之间共享资源。
  在继续执行任何操作前,需要确保已经为资源字典提供了正确的名称。为了让这种技巧生效,必须将资源字典放置到generic.xaml文件中,并且必须将该文件放到应用程序文件夹的Themes子文件夹种。generic.xaml文件种的资源被认为是默认主题的一部分,并且它们总是可用的。
  下一步是为存储在ResourceLibrary程序集中希望共享的资源创建键名。当使用ComponentResourceKey时,需要提供两部分信息:类库程序集中的引用和描述性的资源ID。类引用是WPF允许和其他程序集共享资源的关键部分。放使用资源时,需要提供相同的类引用和资源ID.
  该类的实际外观并不重要,它不需要包含代码.定义该类型的程序集就是ComponentResourceKey将要从中查找资源的程序集。

public class CustomResources
{
}

  现在可以使用该类和资源ID创建键名:

x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomResource}}, ResourceId=SadTitleBrush}"

  下面时generic.xaml文件的完整标记,它包含了一个单独资源——一个使用不同图形的ImageBrush对象:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:ResourceLibrary">
    <ImageBrush
        x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomResources}, ResourceId=SadTileBrush}"
        TileMode="Tile"
        ViewportUnits="Absolute" Viewport="0 0 32 32"
        ImageSource="ResourceLibrary;component/sadface.jpg" Opacity="0.3">
    </ImageBrush>
</ResourceDictionary>

  使用库资源:

<Window x:Class="Resources.ResourceFromLibrary"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:res="clr-namespace:ResourceLibrary;assembly=ResourceLibrary"
        Title="ResourceFromLibrary" Height="300" Width="300"    
>
    <Window.Resources>
        <ImageBrush x:Key="TileBrush" TileMode="Tile"
                    ViewportUnits="Absolute" Viewport="0 0 32 32"
                    ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
    </Window.Resources>
    <StackPanel Margin="5">
        <Button Background="{StaticResource TileBrush}" Padding="5"
                FontWeight="Bold" FontSize="14" Margin="5"
        >A Resource From This Assembly</Button>

        <Button Background="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type res:CustomResources}, ResourceId=SadTileBrush}}"
                Padding="5" Margin="5"
                FontWeight="Bold" FontSize="14">
            A Resource From ResourceLibrary</Button>

        <Button Background="{DynamicResource {x:Static res:CustomResources.SadTileBrush}}"
                Padding="5" Margin="5"
                FontWeight="Bold" FontSize="14">
            A Resource From ResourceLibrary
        </Button>
    </StackPanel>
</Window>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值