依赖属性是标准.NET属性的全新实现,是专门为 WPF 创建的。依赖属性可支持WPF中的样式设置、数据绑定、继承、动画及默认值。
创建依赖属性
第一步时定义表示属性的对象,它是 DependencyProperty 类的实例。属性信息应该始终保持可用,甚至可能在多个类之间共享这些信息,因此必须将 DependencyProperty 对象定义为与其相关联的类的静态字段。
public class FrameworkElement: UIElement, ...
{
public static readonly DependencyProperty MarginProperty;
}
依赖属性字段名称以 "Property" 结尾。字段的定义使用了 readonly 关键字,意味着只能在 FrameworkElement 类的静态构造函数中对其进行设置。
注册依赖属性
WPF确保 DependencyProperty 对象不能直接被直接实例化,因为没有公有的构造函数。使用静态的 DependencyProperty.Register() 方法创建实例。
static FrameworkElement()
{
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(
new Thickness(),FrameworkPropertyMetadataOptions.AffectsMeasure
);
MarginProperty = DependencyProperty.Register("Margin",typeof(Thickness),typeof(FrameworkElement),metadata,new ValidateValueCallback(FrameworkElement.IsMarginValid));
}
要素说明:
- 属性名(Margin)
- 属性使用的数据类型(Thickness结构)
- 拥有该属性的类型(FrameworkElement类)
- 一个具有附加属性设置的 FrameworkPropertyMetadata 对象,该要素是可选的
- 一个用于验证属性的回调函数,该要素是可选的
添加属性包装器
依赖属性是使用在 DependencyObject 基类中定义 GetValue() 和 SetValue() 方法。
public Thickness Margin
{
set { SetValue(MarginProperty,value);}
get { return (Thickness)GetValue(MarginProperty);}
}
当创建属性封装器时,应当只包含对 SetValue() 和 GetValue() 方法的调用,不应该添加任何验证属性值的额外代码,引发事件的代码等。因为WPF中的其他功能可能会忽略属性封装器,并直接调用 SetValue() 和 GetValue() 方法。应当通过 DependencyProperty.ValidateValueCallback 回调函数进行。
使用依赖属性
依赖属性的两个关键行为 - 更改通知 和 动态值识别。
- 当属性值变化时,依赖属性不会自动引发事件以通知属性值发生变化。相反,它们会触发受保护的名为 OnPropertyChangedCallback() 的方法。该方法通过两个WPF服务(数据绑定和触发器)传递信息,并调用 PropertyChangedCallback 回调函数。
- 本质上,依赖属性依赖于多个属性提供者,每个提供者都有各自的优先级。当从属性检索值时,WPF属性系统会通过一些列步骤获取最终值。
- 默认值(FrameworkPropertyMetadata对象设置的值)
- 继承而来的值
- 来自主题样式的值
- 来自项目样式的值
- 本地值(使用代码或XAML直接为对象设置的值)
共享的依赖项属性
尽管一些类有不同的继承层次,但会共享同一依赖项属性。例如,TextBlock.FontFamily属性和 Control.FontFamily 属性指向同一个静态的依赖项属性,该属性实际上是在 TextElement 类的静态构造函数注册该属性。TextBlock 类和 Control 类的静态构造函数只是通过调用 DependencyProperty.AddOwner() 方法重用该属性。
TextBlock.FontFamilyProperty = TextElement.FontFmamilyProperty.AddOwner(typeof(TextBlock));
附加的依赖项属性
为定义附加属性,需要使用 RegisterAttached() 方法,而不是使用 Register() 方法。
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(0,new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged));
Grid.RowProperty = DependencyProperty.RegisterAttached("Row",typeof(int),typeof(Grid),metadata,new ValidateValueCallback(Grid.IsIntValueNotNegative));
属性验证
在WPF中通过属性设置器验证是不合适的,可以通过下面两种方法设置:
- ValidateValueCallback : 该回调函数可接收或拒绝新值。通常用于捕获违反属性约束的明显错误。
- CoerceValueCallback : 该回调函数可将新值修改为更能被接受的值。通常用于处理为相同对象设置的依赖项属性相互冲突的问题。
当试图设置依赖项属性时的过程:
- 首先,CoerceValueCallback 方法有机会修改提供的值,或返回 DependecyValueCallback
- 接下来激活 ValidateValueCallback 方法。不能访问设置属性的实际对象
- 最后,如果前两个阶段都成功,会触发 PropertyChangedCallback 方法