WPF 学习笔记 - 7. Resource

1. 二进制资源

WPF 支持三种方式的二进制资源,这些资源可以非常方便地在 XAML 中使用。

  • Resource: 将资源嵌入程序集中,和 Embedded Resource 有点像。区别在于 WPF 将相关资源打包到 .Resources 文件,然后再由编译器嵌入到程序集文件中。WPF 默认的 URI 访问方式是不支持 Embedded Resource 的。
  • Content: 资源不会嵌入到程序集,仅仅在程序集清单中添加一条记录,这和以往我们所熟悉的方式一样。资源文件必须随其他程序集文件一起部署到目标目录。
  • Loose File: 这类资源通常是运行期动态确定或加入的。
uploads/200811/01_131926_5.png


WPF 通过统一资源标识符(URI)访问相关资源文件。

(1) 访问 Resource / Content 资源
<Image Source="/a.png" />
<Image Source="/xxx/x.png" />

(2) 访问松散资源文件(Loose File)
<Image Source="pack://siteOfOrigin:,,,/a.png" />
<Image Source="c:/test/a.png" />

XAML 编译时需要验证所有资源有效性,因此在使用松散资源文件时必须使用有效的 URI 路径。 "pack://" 表示 Package URI,"pack://siteOfOrigin:,,," 表示从部署位置开始,相应的还有 "pack://application:,,," 上面的 Resource 资源全路径应该是 "pack://application:,,,/a.png",只不过通常情况下我们使用省略写法而已。
<Image Source="pack://application:,,,/a.png" />

其他的 URI 写法还包括:
<Image Source="http://www.qidian.com/images/logo.gif" />
<Image Source="//server1/share/logo.gif" />
<Image Source="file://c:/test/logo.gif" />

(3) 访问其他程序集中的资源文件

提供一个可替换的专用资源 DLL 也是一种常用编程手法,尤其是多语言或者换肤机制。WPF 访问其他程序集资源文件的语法有点古怪。
pack://application:,,,/AssemblyReference;Component/ResourceName
  • 其他程序集的资源必须以 Resource 方式嵌入。
  • 不能省略 "/AssemblyReference" 前面的反斜杠。
  • Component 是关键字,必须包含在 URI 中。
<Image Source="pack://application:,,,/Learn.Library;component/s.gif" />
<Image Source="/Learn.Library;component/s.gif" />

2. 逻辑资源

逻辑资源是一些存储在元素 Resources 属性中的对象,这些 "预定义" 的对象被一个或多个子元素所引用或共享。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <ContentControl x:Key="label1">Hello, World!</ContentControl>
    <ImageSource x:Key="image1">/a.png</ImageSource>
  </Window.Resources>
  <Grid>
    <Label Content="{StaticResource label1}" />
    <Image Source="{StaticResource image1}" />
  </Grid>
</Window>

当然,我们也可以写成下面这种格式。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <ContentControl x:Key="label1">Hello, World!</ContentControl>
    <ImageSource x:Key="image1">/a.png</ImageSource>
  </Window.Resources>
  <Grid>
    <Label>
      <Label.Content>
        <StaticResource ResourceKey="label1" />
      </Label.Content>
    </Label>
    <Image>
      <Image.Source>
        <StaticResource ResourceKey="image1" />
      </Image.Source>
    </Image>
  </Grid>
</Window>

我们甚至可以用逻辑资源定义一个完整的子元素。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <Label x:Key="label1" Content=" Hello, World!" />
    <Image x:Key="image1" Source="/a.png" />
  </Window.Resources>
  <Grid>
    <StaticResource ResourceKey="label1" />
    <StaticResource ResourceKey="image1" />
  </Grid>
</Window>

逻辑资源有个限制,那就是这些继承自 Visual 的元素对象只能有一个父元素,也就是说不能在多个位置使用,因为多次引用的是同一个对象实例。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <Image x:Key="image1" Source="/a.png" />
  </Window.Resources>
  <Grid>
    <StaticResource ResourceKey="image1" />
    <StaticResource ResourceKey="image1" />
  </Grid>
</Window>

你将看到下面这样一个错误提示。

uploads/200811/01_131520_1.png


解决方法就是添加 "x:Shared=False",这样每次引用都会生成一个新的逻辑资源对象。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <Image x:Key="image1" x:Shared="False" Source="/a.png" />
  </Window.Resources>
  <Grid>
    <StaticResource ResourceKey="image1" />
    <StaticResource ResourceKey="image1" />
  </Grid>
</Window>

资源查找: 引用逻辑资源时首先会查找父元素 Resources 集合,如未找到,会逐级检查更上层的父元素。如果直到根元素依然未找到有效的逻辑资源定义,那么 WPF 会检查 Application.Resources (App.xaml 中定义) 和系统属性集合(SystemParamters 等)。每个独立的资源字典中键名不能重复,但在多个不同层级的资源字典中允许重复,离资源引用最近的那个逻辑资源项被优先采纳。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <Label x:Key="label1" Content=" Hello, World!" />
  </Window.Resources>
  <Grid>
    <Grid.Resources>
      <Label x:Key="label1" Content=" Hello, C#!" />
    </Grid.Resources>
    <StackPanel>
      <StaticResource ResourceKey="label1" />
    </StackPanel>
  </Grid>
</Window>

将显示 "Hello, C#!"。

逻辑资源的引用方式又分类 "静态(StaticResource)" 和 "动态(DynamicResource)" 两种方式,区别在于静态引用仅在第一次资源加载时被应用,而动态引用则会在资源被更改时重新应用。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <ContentControl x:Key="label1" x:Shared="False">Hello, World!</ContentControl>
  </Window.Resources>
  <x:Code>
    private void button1_Click(object sender, RoutedEventArgs e)
    {
      this.Resources["label1"] = "Hello, C#!";
    }
  </x:Code>
  <Grid>
    <StackPanel>
      <Label x:Name="label1" Content="{StaticResource label1}" />
      <Label x:Name="label2" Content="{DynamicResource label1}" />
      <Button x:Name="button1" Click="button1_Click">Test</Button>
    </StackPanel>
  </Grid>
</Window>

单击按钮后,你会发现 label2 实时反应了资源的修改。

动态资源只能用于设置依赖属性值,因此它不能像静态资源那样引用一个完整的元素对象。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <Label x:Key="label" x:Shared="False" Content="Hello, World!" />
  </Window.Resources>
  <Grid>
    <StackPanel>
      <DynamicResource ResourceKey="label" />
    </StackPanel>
  </Grid>
</Window>

这将导致出现下图这样的异常,而静态资源则没有这个问题。

uploads/200811/01_131525_2.png


当然,静态资源也有另外一个麻烦,就是不支持前向引用(Forward Reference),也就是说我们必须先定义资源,然后才能使用静态引用。
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Grid>
    <StackPanel>
      <Label Content="{StaticResource label}" />
    </StackPanel>
  </Grid>
  <Window.Resources>
    <ContentControl x:Key="label">Hello, World!</ContentControl>
  </Window.Resources>
</Window>

属于静态资源的异常出现了,当然动态资源是没有这个问题的。

uploads/200811/01_131530_3.png


如果有必要的话,我们可以将逻辑资源分散定义在多个 XAML 文件(Resource Dictionary) 中。

uploads/200811/01_131535_4.png


Dictionary1.xaml
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ContentControl x:Key="label1">Hello, World!</ContentControl>
</ResourceDictionary>

Dictionary2.xaml
<ResourceDictionary 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ContentControl x:Key="label2">Hello, C#!</ContentControl>
</ResourceDictionary>

Window1.xaml
<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1">
  <Window.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Dictionary1.xaml" />
        <ResourceDictionary Source="Dictionary2.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Window.Resources>
  <Grid>
    <StackPanel>
      <Label Content="{DynamicResource label1}" />
      <Label Content="{DynamicResource label2}" />
    </StackPanel>
  </Grid>
</Window>

如果合并的字典中有重复的键值,那么最后加入的资源将优先。比如 Dictionary1.xaml 和 Dictionary2.xaml 都有一个 x:Key="label1" 的资源,那么 Dictionary2.label1 将获胜。

在程序代码中我们可以用 FindResource/TryFindResource 设置静态资源,用 SetResourceReference 设置动态资源。
this.labelA.Content = (ContentControl)this.labelA.FindResource("key1"); // StaticResource
this.labelB.SetResourceReference(Label.ContentProperty, "key2"); // DynamicResource
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章