一:依赖属性的全面解析
听到依赖属性,自然联想到C#中属性的概念。C#中属性是抽象模型的核心部分,而依赖属性是专门基于WPF创建的。在WPF库实现中,依赖属性使用普通的C#属性进行了包装,使得我们可以通过和以前一样的方式来使用依赖属性,但我们必须明确,在WPF中我们大多数都在使用依赖属性,而不是使用属性。依赖属性重要性在于,在WPF核心特性,如动画、数据绑定以及样式中都需要使用到依赖属性。既然WPF引入了依赖属性,也自然有其引入的道理。WPF中的依赖属性主要有以下三个优点:
- 依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量。许多之前需要写很多代码才能实现的功能,在WPF中可以轻松实现。
- 节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地解决了这个问题,它内部实现使用哈希表存储机制,对多个相同控件的相同属性的值都只保存一份。关于依赖属性如何节约内存的更多内容参考:WPF的依赖属性是怎么节约内存的
- 支持多种提供对象:可以通过多种方式来设置依赖属性的值。可以配合表达式、样式和绑定来对依赖属性设置值。
1.1 依赖属性的定义
上面介绍了依赖属性所带来的好处,这时候,问题又来了,怎样自己定义一个依赖属性呢?C#属性的定义大家再熟悉不过了。下面通过把C#属性进行改写成依赖属性的方式来介绍依赖属性的定义。下面是一个属性的定义:
1 public class Person
2 {
3 public string Name { get; set; }
6 }
在把上面属性改写为依赖属性之前,下面总结下定义依赖属性的步骤:
- 让依赖属性的所在类型继承自DependencyObject类。
- 使用public static 声明一个DependencyProperty的变量,该变量就是真正的依赖属性。
- 在类型的静态构造函数中通过Register方法完成依赖属性的元数据注册。
- 提供一个依赖属性的包装属性,通过这个属性来完成对依赖属性的读写操作。
根据上面的四个步骤,下面来把Name属性来改写成一个依赖属性,具体的实现代码如下所示:
// 1. 使类型继承DependencyObject类
public class Person : DependencyObject
{
// 2. 声明一个静态只读的DependencyProperty 字段
public static readonly DependencyProperty nameProperty;
static Person()
{
// 3. 注册定义的依赖属性
nameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Person),
new PropertyMetadata("Learning Hard",OnValueChanged));
}
// 4. 属性包装器,通过它来读取和设置我们刚才注册的依赖属性
public string Name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e)
{
// 当只发生改变时回调的方法
}
}
从上面代码可以看出,依赖属性是通过调用DependencyObject的GetValue和SetValue来对依赖属性进行读写的。它使用哈希表来进行存储的,对应的Key就是属性的HashCode值,而值(Value)则是注册的DependencyPropery;而C#中的属性是类私有字段的封装,可以通过对该字段进行操作来对属性进行读写。总结为:属性是字段的包装,WPF中使用属性对依赖属性进行包装。