1、理解依赖项属性

“依赖项属性的用途在于提供一种方法来基于其他输入的值计算属性值”。依赖属性是专门针对WPF创建的。但WPF库中的依赖属性都使用普通的.NET属性过程(property procedure)进行封装,就可以通过常规的方式使用。

  1. /// <summary>
  2. /// 私有成员
  3. /// </summary>
  4. private string name = string.Empty;
  5. /// <summary>
  6. /// .net属性
  7. /// </summary>
  8. public string Name
  9. {
  10. get//读访问器
  11. {
  12. return name;
  13. }
  14. set//写访问器
  15. {
  16. name = value;
  17. }
  18. }

2、定义依赖项属性

一般情况下只是使用,但如果要自定义WPF控件或者给现有的WPF控件增加新功能时就需要自定义。
定义时需要用到特殊的语法:

  • 静态的
  • 使用readonly只能在构造时初始化
  • 必须是DependencyProperty类实例
  • 属性名称后面要带上Property

定义一个Margin依赖项属性的例子

  1. public static readonly DependencyProperty MarginProperty;

3、注册依赖项属性
(1)创建FrameworkPropertyMetadata对象,指明通过依赖项属性使用什么服务(支持数据绑定,动画,日志等)

  1. FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure);


(2)调用DependencyProperty.Register()静态方法注册属性
五个参数

  • 属性名
  • 属性使用的数据类型
  • 拥有该属性的类型
  • 具有附加属性设置的FrameworkPropertyMetadata对象(此参数可选)
  • 用于验证属性的回调函数(此参数可选)

在构造函数中

  1. MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(FrameworkElement), metadata, new ValidateValueCallback(FrameworkElement.IsMarginValid));

4、添加属性包装器

使用DependencyObject类的SetValue()和GetValue()方法

  1. public Thickness Margin
  2. {
  3. set { SetValue(MarginProperty, value); }
  4. get { return (Thickness)GetValue(MarginProperty); }
  5. }
  6. //设置属性
  7. element.Margin = new Thickness(5);

5、WPF使用依赖项属性的方式

依赖项属性依赖于多个属性提供者,每个提供者都有其自己的优先级,当从属性检索值时,WPF属性系统会通过一系列步骤获取最终的值,它首先通过以下因素来决定基本值,优先级从低到高:
(1) 默认值(由FrameworkPropertyMetadata对象设置的值)。
(2) 继承而来的值。
(3) 来自主题样式的值。
(4) 来自项目样式的值。
(5) 本地值(即使用代码或直接用XAML直接为对象设置的值)
WPF使用上面的列表确定依赖项属性的基本值,但是基本值未必就是最后从属性中检索到的值,这是因为WPF还需要考虑其他几个可能改变属性值的提供者,下面是WPF决定一个属性值的四步骤过程:
(1) 确定基本值。
(2) 如果属性是用表达式设置的,则对表达式进行求值。
(3) 如果该属性是动画的目标,应用该动画。
(4) 运行 CoerceValueCallback回调函数修正属性值。

6、共享的依赖项属性

一些类会共享同一个依赖项属性,尽管这些类具有不同的继承层次,例如,TextBlock.FontFamily属性和Control.FontFamily属性都指向TextElement类中定义的TextElement.FontFamilyProperty依赖项属性,TextElement类的静态构造函数注册该属性,而TextBlock类和Control类的静态构造函数只是简单的通过调用DependencyProperty.AddOwner()方法重用该属性:

  1. TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

可以使用重载的AddOwner()方法提供一个验证回调函数以及一个仅应用于依赖项属性新用法的新 FrameworkPropertyMetadata对象。
在WPF中重用依赖项属性可以得到一些奇异的效果,最有名的是样式。例如,如果使用样式自动设置TextBlock.FontFamily属性,样式也会影响Control.FontFamily属性,因为在后台这两个类使用同一个依赖项属性。

7、附加的依赖项属性

附加属性也是依赖属性,它和依赖属性的区别是注册附加属性时使用DependencyProperty.RegisterAttached()方法,而不是DependencyProperty.Register()方法。例如:

  1. FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(0, new PropertyChangedCallback(null));
  2. Grid.RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid), metadata, new ValidateValueCallback(null));

8、属性验证

当定义任何类型的属性时,都需要面对错误设置属性的可能性,对于传统的.Net属性,可以尝试在属性设置器中捕获这类问题。但对于依赖属性,这种方法不合适,因为可以通过WPF属性系统使
用SetValue()方法直接设置属性。作为替代,WPF提供了两种方法用于阻止非法值:
ValivateValueCallback:调用该函数可以接受或拒绝新值,通常,它用于捕获违反属性约束的明显错误,可以作为DependencyProperty.Register()方法的一个参数提供该回调函数。
CoerceValueCallback:该回调函数可将新值修改为更能被接受的值。

9、验证回调

DependencyProperty.Register()方法接受一个可选的验证回调:

  1. MarginProperty = DependencyProperty.Register("Margin", typeof(Thickness), typeof(FrameworkElement), metadata, new ValidateValueCallback(FrameworkElement.IsMarginValid));
  2. private static bool IsMarginValid(object value)
  3. {
  4. Thickness thickness = (Thickness)value;
  5. return thickness.IsValid();
  6. }

10、强制回调

通过FrameworkPropertyMetadata对象使用CoerceValueCallback回调,它可以处理相互关联的属性。例如,ScrollBar控件提供了Maximum、Minimum和Value属性,这些属性都继承自RangeBase类,保持对这些属性进行调整的一种方法是使用属性强制。例如,当设置Maximum属性时,必需进行强制,以确保不能小于为Minimum属性的值:

  1. <br />
  1. FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
  2. metadata.CoerceValueCallback = new CoerceValueCallback(CoerceMaximum);
  3. DependencyProperty.Register("Maximum", typeof(double), typeof(RangeBase), metadata);
  4. private static object CoerceMaximum(DependencyObject d, object value)
  5. {
  6. RangeBase base1 = (RangeBase)d;
  7. if (((double)value) < base1.Minimum)
  8. {
  9. return base1.Minimum;
  10. }
  11. return value;
  12. }