04_依赖属性

依赖属性是标准.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属性系统会通过一些列步骤获取最终值。
    1. 默认值(FrameworkPropertyMetadata对象设置的值)
    2. 继承而来的值
    3. 来自主题样式的值
    4. 来自项目样式的值
    5. 本地值(使用代码或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 : 该回调函数可将新值修改为更能被接受的值。通常用于处理为相同对象设置的依赖项属性相互冲突的问题。

当试图设置依赖项属性时的过程:

  1. 首先,CoerceValueCallback 方法有机会修改提供的值,或返回 DependecyValueCallback
  2. 接下来激活 ValidateValueCallback 方法。不能访问设置属性的实际对象
  3. 最后,如果前两个阶段都成功,会触发 PropertyChangedCallback 方法

我的依赖属性

04_依赖属性

上一篇:WPF按钮添加图片


下一篇:(C#/WPF)管理系统源码分享