在类上实现属性时,只要类派生自 DependencyObject,就可以选择使用 DependencyProperty 标识符支持属性,从而使其成为依赖属性。
第一步是定义表示属性的对象,它是 DependencyProperty 类的实例。
属性信息应该始终保持可用,甚至可能需要在多个类之间共享这些信息。因此,必须将 DependencyProperty 对象定义为与其相关联的类的静态字段。
字段的定义使用了 readonly 关键字,这意味着只能在 FrameworkElement 类的静态构造函数中对其进行设置。
public class FrameworkEleent : UIElement
{
public static readonly DependencyProperty MarginProerty;
}
在与其关联的类的静态构造函数中进行。
WPF 确保 DependencyProperty 对象不能被直接实例化,因为 DependencyProperty 类没有公有的构造函数。相反,只能使用静态的 DependencyProperty.Register()方法创建 DependcyProperty 实例。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));
}
注册依赖项属性需要经历两个步骤。
首先创建 FrameworkPropertyMetadata 对象,该对象指示希望通过依赖项属性使用什么服务(如支持数据绑定、动画以及日志)。接下来通过调用 DependencyProperty.Register() 静态方法注册属性。
在这一步骤中,您负责提供以下几个要素:
使用 FrameworkPropertyMetadata 对象配置创建的依赖项属性的附加功能。
FrameworkPropertyMetadata 类的大多数属性是简单的 Boolean 标志,通过设置这些属性来翻转某项功能(每个 Boolean 标志的默认值为 false)。只有少数几个是指向用于执行特定任务的自定义方法的回调函数,其中一个是 FrameworkPropertyMetadata.Defaultvalue,用于设置在第一次初始化属性时 WPF 将要应用的默认值。
最后一步:使用传统的.NET 属性封装 WPF 依赖项属性。
WPF 属性的属性过程是使用在 DependencyObject 基类中定义的 GetValue() 和 SetValue() 方法。
public Thickness Margin
{
set{SetValue(MarginProperty,value);}
get{return (Thickness)GetValue(MarginProperty);}
}
当创建属性封装器时,应当只包含对 SetValue() 和 GetValue() 方法的调用。
注意: 属性封装器不是验证数据或引发事件的正确位置,而是通过DenpendencyProperty.ValidateValueCallback 回调函数进行验证操作(即不应当在 set 和 get 里附加此类代码逻辑)。
若希望删除本地值设置,并像从来没有设置过那样确定属性值,则不能通过设置一个新值来实现。而是需要使用另一个继承自 DependencyObject 类的方法:ClearValue()。
该方法的用法:
myElement.ClearValue(FrameworkElement.MarginProperty);
使用依赖项属性的功能都是通过每个依赖项属性都支持的两个关键行为进行工作:更改通知和动态值识别。
更改通知: 当属性值发生变化时,依赖项属性不会自动引发事件以通知属性值发生了变化。相反,它们会触发受保护的名为 OnPropertyChangedCallback() 的方法。该方法通过两个 WPF 服务(数据绑定和触发器)传递信息,并调用 ProertyChangedCallback 回调函数。
动态值识别: 当从依赖项属性检索值时,WPF 需要考虑多个方面。
1)共享的依赖项属性
一些类会共享同一个依赖项属性,尽管这些类具有不同的继承层次。
例如,TextBlock.FontFamily 属性和 Control.FontFamily 属性指向同一个静态的依赖项属性,该属性实际上是在 TextElement 类中定义的 TextElement.FontFamilyProperty 依赖项属性,TextElement 类的静态构造函数注册该属性,而 TextBlock 类和 Control 类的静态构造函数只是简单通过 DependencyProperty.AddOwner()方法重用该属性:
TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));
2)附加的依赖项属性
定义附加的依赖项属性
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(
0,new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged));
Grid.RowProperty = DenpencyProperty.RegisterAttached("Row",typeof(int),
typeof(Grid),metadata,new ValidateValueCallback(Grid.IsIntValueNotNegative));
当创建附加属性时,不需要定义.NET属性包装器。