jk's notes
  • ch09 属性

ch09 属性

前面章节出现了近100个示例, 里面隐藏了很多细节, 主要是为了能将案例演示出来. 本章主要解释其中一个主题, 即属性.

属性基础

按照 OOP 的方式解释属性的作用与含义. 这里是按照传统的属性进行介绍的. 属性的定义基本上都是基础类型, 然后例举了 C# 代码与 XAML 代码怎么创建与初始化对象与属性的.

但是 WPF 的属性远比其复杂的多, 特别是在 XAML 中. 背后存在一个类型转换的过程.

类型转换

例如代码:

<Label Background="Yellow" Content="Hello" />

其中属性 Background 并不是简单的字符串, 这里的 Yellow 是 System.Windows.Media.Brush 类的实例. 例如在 C# 代码中:

grdMain.Background = Brushes.Yellow;

这里没有设置字符串, 而是使用静态类 Brushes 提供的属性 Yellow. 它返回的是 SolidColorBrush 对象, 也是 Brush 类型的. 实际上 XAML 中利用了类型转换, 将字符串转换成了对象.

XAML 为很多基础属性提供了类型转换, 例如数字的 (Width, Height), 点对象数组的 (Polygon 的 Points 属性), 数字数组的 (DashArray), 字符串的 (例如使用字符串设置 Content 属性), 简单颜色的 (Background, Stroke, Fill), 布尔类型的 (IsChecked, IsEnabled), 简单画刷的 (BorderBrush), 等等.

XAML 甚至还包含到复杂命令序列的转换, 例如 Path mini-language 中的 Data.

但是在 XAML 中处理更为复杂的对象需要构造子对象的形似来处理, 并非所有的都可以转换.

属性元素语法

属性元素语法使用 "组件名.属性名" 作为标签来定义属性, 其子节点为属性值. 例如:

<Grid>
  <Grid.Background>
    <RadialGradientBrush>
      <GradientStop Color=”#FFFFFFFF” Offset=”0.00”/>
      <GradientStop Color=”#FFFF0000” Offset=”0.25”/>
      <GradientStop Color=”#FFFFFF00” Offset=”0.50”/>
      <GradientStop Color=”#FF00FF00” Offset=”0.75”/>
      <GradientStop Color=”#FF0000FF” Offset=”1.00”/>
    </RadialGradientBrush>
  </Grid.Background>
</Grid>

然后作者对上述代码的含义进行了一一解释. 并说明其中的 Color 与 Offset 是简单的属性值, 只用默认的类型转换进行了处理. 然后在 C# 中也能实现同样的内容:

RadialGradientBrush br = new RadialGradientBrush();
br.GradientStops.Add(new GradientStop(Color.White, 0.00));
br.GradientStops.Add(new GradientStop(Color.Red, 0.25));
br.GradientStops.Add(new GradientStop(Color.Yellow, 0.50));
br.GradientStops.Add(new GradientStop(Color.Lime, 0.75));
br.GradientStops.Add(new GradientStop(Color.Blue, 1.00));
grdBackground.Background = br;

然后作者依旧就上述的 C# 代码进行了详细解释.

属性元素语法允许在 XAML 中构建复杂的对象, 前面介绍过的 Grid.ColumnDefinitions, Grid.RowDefinition, 以及 BitmapEffect, Header, ContentMenu, LayoutTransform 就是如此.

然后对这些属性进行了说明:

  • BitmapEffect 用于描述控件是否显示阴影, 浮雕表面, 斜面等其他效果. 它必须是一个对象, 因此使用 XAML 的属性元素语法.
  • Header 在菜单等组件中, 用来构建非字符串的标题信息时使用属性元素语法.
  • LayoutTransform 描述控件如何移动, 拉伸, 以及旋转. 该属性是一个复杂对象, 使用属性元素语法.

然后作者给出了一个比较复杂的案例, 融合的上述的属性. 展示了核心代码后, 并用一定篇幅对代码进行了解释.

这里引出两个转换, 一个是 LayoutTransform, 另一个是 RenderTransform. 他们都可以对控件进行变换. 区别是对布局空间的影响. 前者变换后, 控件的占用空间会变化, 并影响周围元素的布局. 后者不会对周边元素的布局造成影响.

属性继承

首先介绍了 OOP 模式下的继承含义. 并描述了属性, 方法等继承的特征.

然后说明 WPF 采用另一种集成规则, 组件会继承容器的属性值.

例如给容器设置 FontSize, 其容器中的组件就会有该属性的值. 但是属性的容器继承存在很多例外, 不能完全遵守, 所以不能太依赖属性继承. 然后作者举了一个例子加以说明.

然后作者演示了一个案例, 并演示了一些可以被继承的属性, 以及一些不被继承的属性.

这一节的目的是需要你有一个意识, 属性可以被继承, 且继承自容器, 但不是所有属性都会继承.

附加属性 (Attached Property)

有些时候, 控件的某个属性需要使用到另一个控件的信息. 例如在 Grid 中的 Button, 布局时指定 Row 与 Column.

实际上 Button 等控件不应该有 Row 与 Column 属性. 因为仅在 Grid 中才需要, 但这不是必须的. 同时不同的布局控件, 带来的属性也不同, 例如 Canvas, DockPanel 等. 不可能给控件提供所有的这些属性. 如果要给它提供这些属性, 那么其他控件是否也需要? 显然不合适. 同时控件的属性是需要占据存储空间的, 这么设置显然不合适.

这么添加从更新上来说也不合适, 若提供了新的布局控件, 那是不是要将所有其他子控件都重新构建一遍? 这也是不合适的.

WPF 采用了另一种方式来实现, 即附加属性.

然后作者给出了一个语法描述: 使用属性提供者, 加点, 然后跟上属性名.

然后给出一个案例, 说明了 Grid 布局, 并指出只有直接包含在 Grid 中的控件可以使用 Grid.Row 和 Grid.Column. 另外, 附加属性没有严格的约束校验. 语法上可以随意使用, 但无效的会被忽略.

在深入浅出中探究了附加属性的本质. 就是一个静态属性, 和背后的一个哈希表.

小结 (略)

本章似乎是一个介绍性的章节.

Last Updated:
Contributors: jk