WPF/MVVM系列(3)——属性

.NET中有三种属性:C#语言层面的普通属性(以下简称“C#常规属性”)、WPF依赖属性、WPF附加属性。

C#常规属性的缘由:封装是面向对象编程的一大特性,其表现形式就是类。类通常包含若干字段,这些字段一般不直接对外开发,而是通过GetXXX/SetXXX这对方法来间接访问,这样就可以进行数据合法性检测。C#语言非常人性化,将GetXXX/SetXXX这对方法抽象成一个新的语言特性——属性。

WPF依赖属性和附加属性,从语言语法角度来看没有额外创新,属于.NET标准库层面的进一步封装。

WPF依赖属性

作用

(1)解决存在大量类实例时C#属性内存使用过大的问题

由于属性显式或隐式地映射一个字段,当一个类中包含多个属性且在程序中大量实例化该类时,这些属性就会占据(类实例数量)x(属性内存消耗)的内存。

为了满足精细化UI控制要求,WPF的布局组件或控件属性众多,以上内存占用问题就相对突出,怎么解决呢?

WPF创造了依赖属性,依赖属性的特点是:一个类实例如果没有显式给依赖属性赋值,那么就是用全局默认值,这时不会占据内存;如果显式赋值了,就会另外存储。

(2)为WPF的高级特性(例如绑定)提供底层支持

设置绑定时,数据源种类很多,但是绑定目标必须继承自依赖对象(DependencyObject),且绑定属性通常是依赖属性。

使用

使用依赖属性很简单,新建类,继承DependentObject;在类中键入propdp,按两次tab按键,就会生成定义依赖属性的模板;设置属性类型(如下为string)、属性名(如下为Name)、补全typeof(Student)中的类名、补全默认值(如下为lh)。

使用C#属性对依赖属性进行包装,使用时与普通属性别无二致(记住这一点,使用它时大脑就会保持清醒)。

1
2
3
4
5
6
7
8
9
10
public class Student : DependencyObject {
public string Name {
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}

// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Student), new PropertyMetadata("lh"));
}

WPF附加属性

作用

通常有这样的需求:相同的一个类实例,在不同环境下可能有不同的语境属性,例如一个人在学校有“班级”属性,在公司有“职称”属性。

但不可能在定义“人”这个类时,一次性添加所有可能的属性;这时可以使用附加属性。

附加属性定义在环境类中(如上面的“学校”类或“公司”类),用环境类的静态方法设置属性值,传入“人”的类实例就可以。

使用

使用附加属性很简单,新建类,继承DependentObject;在类中键入propa,按两次tab按键,就会生成定义附加属性的模板;设置属性类型(如下为int)、属性名(如下为Grade)、补全typeof(School)中的类名、补全默认值(如下为1)。

会使用两个静态方法对获取和设置附加属性的操作进行包装,都需要传递一个依赖对象参数,最后使用依赖对象来直接调用GetValue/SetValue方法;而在依赖对象中,直接由this调用GetValue/SetValue方法,两者原理基本一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class School : DependencyObject {
public static int GetGrade(DependencyObject obj) {
return (int)obj.GetValue(GradeProperty);
}

public static void SetGrade(DependencyObject obj, int value) {
obj.SetValue(GradeProperty, value);
}

// Using a DependencyProperty as the backing store for Grade. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GradeProperty =
DependencyProperty.RegisterAttached("Grade", typeof(int), typeof(School), new PropertyMetadata(1));
}

总结

WPF的依赖属性和附加属性设计得相当巧妙(不愧是微软的大牛们,他们总能为软件世界贡献自己的无穷智慧),弥补了C#常规属性在某些场景下的不足;但在形式上又尽可能与C#常规属性保持一致!

我认为,学习WPF的依赖属性和附加属性,明白它们的作用最为重要;其次,在使用和理解它们时,能够在大脑里化繁为简,这样就能游刃有余!

至于源码,本人就不做深入探究了,呵呵!

评论