jk's notes
  • ch12 资源

ch12 资源

开发中有一个很重要的概念便是复用. 然后作者描述了复用涉及到的语法特征. 然后描述了复用的好处.

  • 只写一次代码
  • 修改代码减少 bug
  • 修改代码不会漏
  • 功能类似可以拷贝
  • 写得更少

函数与方法是 C# 复用的手段, 资源是 XAML 复用的方式. 它将需要在 XAML 中反复使用的数据定义到 XAML 文件中.

CSS 有异曲同工之妙

本章介绍怎么定义资源, 怎么引用资源.

作者补充了一个说明, 过度封装也不好.

定义资源

定义并使用一个简单的资源是很容易的. 实际上举一个例子就行, 比用描述更容易理解.

  • 在对象, 例如 Window, Grid 或其他容器中添加 Resource 属性. 每一个资源都是一个对象或简单值.
  • 需要给每一个资源提供唯一的 x:key 属性, 用于定位该资源.

例如:

<Window.Resources>
  <LinearGradientBrush x:Key="brButton" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="Red" Offset="0"/>
    <GradientStop Color="White" Offset="0.5"/>
    <GradientStop Color="Blue" Offset="1"/>
  </LinearGradientBrush>
  <BitmapEffectGroup x:Key="bmeButton">
    <DropShadowBitmapEffect/>
  </BitmapEffectGroup>
</Window.Resources>

任何包含在含有 Resource 元素的标签中的元素, 都可以使用该资源. 如上代码, 任何在 Window 中的组件都可以使用该资源.

在已经定义这样简单的属性资源后, 你可以利用属性的特性语法来使用它. 你所提供的特性应该使用下面格式 {StaticResource 资源名}.

然后作者描述了随书示例的作用. 并且说明了使用资源可以将反锁复杂的属性赋值变成简单的属性赋值. 还能复用. 同时如果需要批量修改也十分容易.

注意, 这里 BitmapEffect 属性已过时, 建议使用 Effect 代替.

资源类型

资源可以是任意类型的. 可以将其分为 Normal 属性值, 控件, 以及简单类型.

Normal 属性值

Normal 属性值实际上就是前面看到的哪些, 例如背景色, 填充渐变, 效果, 线宽, 字体等.

控件

资源也可以是一个控件, 例如放在按钮中的文本内容, 可以作为 TextBlock 定义到资源中, 并给定名字, 使用的时候使用 Content="{StaticResource xxx}" 来引用该资源.

在或者将上下文菜单 (ContentMenu) 定义成资源, 然后再复用到不同的控件上.

简单数据类型

XAML 只能读懂组件, 那么 C# 的简单数据类型该怎么描述呢?

  • 它需要引入命名空间 System.
  • 然后使用类型作为标签使用来定义类型.
<Window ...
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        ...>
  <Window.Resource>
  	<sys:Double x:Key="num1">123.456</sys:Double>
  </Window.Resource>
</Window>

可以通过对象查看器来查看有哪些类型可以使用.

资源层级结构

WPF 应用内在存在一个树结构, 控件在使用资源的时候, WPF 会从当前控件 (树的子节点) 向树的根节点来查找对应的资源, 其查询路径到 Window 后会向顶级 Application 去查找. 一旦知道该资源, 就会立即停止查找, 并使用该资源. 如过一直没有查找到, 则会报错.

注意资源对应数据和属性的类型必须严格遵守.

然后作者描述了一个多层级, 并且每个层级中包含一部分资源的案例.

合并资源字典

简单来说, 就是单独创建一个 xaml 文件, 专门存放资源. 其格式与 App.xaml 中的 Application 一样, 可以将其属性拷贝出来, 新的 xaml 文件中, 使用 ResourceDictionary 标签作为根节点, 也是资源的容器.

要加载该资源的 xaml 文件, 只需要在 Resource 下使用 ResourceDictionary, 并使用 MergeDIctionaries 属性, 该属性是一个集合, 并提供 ResourceDictionary 组件, 该组件使用 Source 来加载资源文件.

并且 MergeDictionaries 是一个集合, 可以存入多个 ResourceDictionary, 如果多个 ResourceDictionary 中存在同名的资源, 后加载的会覆盖先加载的资源, 并生效.

例如: 单独的资源文件: resource.xaml

<ResourceDictionary xmlns="..."
                    xmlns:x="..."
                    xmlns:sys="...">
  ...
</ResourceDictionary>

加载资源使用:

<Window.Resource>
	<ResourceDictionary>
  	<ResourceDictionary.MergeDictionaries>
    	<ResourceDictionary Source="src1.xaml" />
    	<ResourceDictionary Source="src2.xaml" />
    </ResourceDictionary.MergeDictionaries>
  </ResourceDictionary>
</Window.Resource>

动态资源

之前的资源都是使用 {StaticResource 资源名} 来绑定加载的资源. 该绑定只会执行一次. 如果 XAML 树加载顺序在绑定之后, 那么会出现无法获得资源的情况, 并且也不会因在运行时资源发生变化而更新 UI. 因此引入动态资源.

动态资源会在需要时进行查找 (有点懒加载的感觉, 需要用的时候去查询资源). 因此动态资源不仅不会因为查询不到无法绑定数据, 而且会在资源发生变化时更新使用资源的组件.

语法上使用 {DynamicResource 资源名}.

动态资源会消耗额外的性能, 开发中应尽可能使用静态资源.

然后作者给出一个适用案例, 系统资源修改的案例, 在打开资源的时候, 调整配置, 界面立即发生变化.

然后作者给定一个案例, 该案例使用系统颜色, 使用了在资源中定义了时间.

  • 系统颜色使用 SystemColors.DesktopColorKey 与 SystemColors.ActiveCaptionBrushKey.
  • 资源可以使用 控件.Resources 属性来操作: 移除现有资源 (Remove), 添加新资源 (Add).

由于需要修改 UI 上的内容, 案例中使用了计时器, 是在另一个线程中触发执行. 因此代码中使用了:

this.Dispatcher.Invoke(new Action(SetTimeResource));

小结

略

Last Updated:
Contributors: jk