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
. 另外, 附加属性没有严格的约束校验. 语法上可以随意使用, 但无效的会被忽略.
在深入浅出中探究了附加属性的本质. 就是一个静态属性, 和背后的一个哈希表.
小结 (略)
本章似乎是一个介绍性的章节.