依赖属性元数据

Windows Presentation Foundation (WPF) 属性系统包括依赖属性元数据报告系统。 通过元数据报告系统提供的信息超过了通过反射或通用公共语言运行时 (CLR) 特征提供的信息。 注册依赖属性时,可以选择创建和分配元数据。 如果您是从定义依赖属性的类派生的,则可以重写继承的依赖属性的元数据。 并且,如果将自己的类设为某个依赖属性的所有者,则可以重写该继承依赖属性的元数据。

先决条件

本文假设对依赖属性有一个基本的了解,并且你已阅读 依赖项属性概述。 若要遵循本文中的示例,如果熟悉可扩展应用程序标记语言(XAML),并且知道如何编写 WPF 应用程序,则很有帮助。

如何使用元数据

可以查询依赖属性元数据来检查依赖属性的特征。 当属性系统处理依赖属性时,它将访问其元数据。 依赖属性的元数据对象包含以下类型的信息:

  • 依赖属性的默认值,当没有其他值应用时,由属性系统设置,例如本地、样式或继承值。 有关在运行时分配依赖项属性值期间的值优先级的详细信息,请参阅 Dependency 属性值优先级

  • 对所有者类型的强制值回调和属性更改回调的引用。 您只能获取具有 public 访问修饰符或在您允许的访问范围内的回调的引用。 有关依赖属性回调的详细信息,请参阅 Dependency 属性回调和验证

  • WPF 框架级依赖属性特征(如果依赖属性是 WPF 框架属性)。 WPF 进程(如框架布局引擎和属性继承逻辑)查询 WPF 框架级元数据。 有关详细信息,请参阅 Framework 属性元数据

元数据 API

PropertyMetadata 类存储属性系统使用的大多数元数据。 可以通过以下方式创建和分配元数据实例:

  • 向属性系统注册依赖属性的类型。

  • 从定义依赖属性的类继承的类型。

  • 类型将自己添加为依赖属性所有者。

如果类型在不指定元数据的情况下注册依赖属性,则属性系统会将具有该类型的默认值的对象分配给 PropertyMetadata 依赖属性。

若要检索依赖属性元数据,请在标识符上调用GetMetadata的其中一个DependencyProperty重载。 元数据作为 PropertyMetadata 对象返回。

不同的体系结构区域存在派生自 PropertyMetadata的更具体的元数据类。 例如, UIPropertyMetadata 支持动画报告,并支持 FrameworkPropertyMetadata WPF 框架属性。 依赖项属性也可以注册到 PropertyMetadata 派生类。 尽管 GetMetadata 返回的是 PropertyMetadata 对象,但在适用的情况下,可以强制转换为派生类型以检查特定类型的属性。

FrameworkPropertyMetadata 公开的属性特征有时称为标记。 创建 FrameworkPropertyMetadata 实例时,可以选择将枚举类型的 FrameworkPropertyMetadataOptions 实例传递到 FrameworkPropertyMetadata 构造函数中。 FrameworkPropertyMetadataOptions 允许按位组合指定元数据标志。 FrameworkPropertyMetadata使用FrameworkPropertyMetadataOptions来保持其构造函数签名的长度合理。 在依赖属性注册时,在FrameworkPropertyMetadataOptions上设置的元数据标志会作为属性公开在FrameworkPropertyMetadataBoolean中,而不是按位组合的标志,以使查询元数据特征更加直观。

替代或创建新元数据?

继承依赖属性时,可以通过重写依赖属性的元数据来更改依赖属性的特征。 但是,你可能并不总是能够通过重写元数据来实现依赖属性方案,有时需要使用新元数据在类中定义自定义依赖属性。 自定义依赖属性的功能与 WPF 类型定义的依赖属性相同。 有关详细信息,请参阅 自定义依赖项属性

无法重写的依赖属性的一个特征是其值类型。 如果继承的依赖属性具有所需的近似行为,但方案需要不同的值类型,请考虑实现自定义依赖属性。 可以通过类型转换或其他派生类中的实现链接属性值。

重写元数据的方案

重写现有依赖属性元数据的示例方案包括:

  • 更改默认值,这是一种常见方案。

  • 更改或添加属性更改回调,如果继承的依赖属性与其他依赖属互的方式不同于其基本实现,则可能需要此回调。 支持代码和标记的编程模型的特征之一是属性值可以按任意顺序设置。 此因素可能会影响实现属性更改回调的方式。 有关详细信息,请参阅 依赖项属性的回调和验证

  • 更改 WPF 框架属性元数据 选项。 通常,元数据选项是在注册新的依赖属性期间设置的,但你可以在OverrideMetadataAddOwner调用中重新指定它们。 有关重写框架属性元数据的详细信息,请参阅 Specifying FrameworkPropertyMetadata。 有关如何在注册依赖属性时设置框架属性元数据选项,请参阅 自定义依赖属性

注释

由于验证回调不是元数据的一部分,因此无法通过重写元数据来更改它们。 有关详细信息,请参阅 验证值回调

重写元数据

在实现新的依赖属性时,可以通过使用Register方法的重载来设置其元数据。 如果类继承依赖属性,则可以使用 OverrideMetadata 该方法替代继承的元数据值。 例如,您可以使用 OverrideMetadata 来设置特定类型的值。 有关详细信息和代码示例,请参阅 重设依赖属性的元数据

WPF 依赖属性的示例为 Focusable. FrameworkElement 类注册 Focusable。 该 Control 类派生自 FrameworkElement,继承 Focusable 依赖属性,并重写继承的属性元数据。 重写会将默认属性值从false更改为true,但保留其他继承的元数据值。

由于大多数现有依赖属性不是虚拟属性,因此其继承的实现会隐藏现有成员。 重写元数据特征时,新元数据值要么替换原始值,要么将其合并:

  • 对于 DefaultValue,新值将替换现有的默认值。 如果在重写元数据中未指定 DefaultValue ,则该值来自在元数据中指定 DefaultValue 的最近上级。

  • 对于 a PropertyChangedCallback,默认合并逻辑将存储表中的所有 PropertyChangedCallback 值,并在属性更改时调用所有值。 回调顺序由类深度确定,其中层次结构中基类注册的回调将首先运行。

  • 对于 a CoerceValueCallback,新值将替换现有 CoerceValueCallback 值。 如果没有在替代元数据中指定 CoerceValueCallback ,该值将来自元数据中指定的最近的祖先 CoerceValueCallback

注释

默认合并逻辑由 Merge 方法实现。 你可以在继承依赖属性的派生类中指定自定义合并逻辑,方法是替代该类中的 Merge

作为所有者添加一个类

若要“继承”在不同类层次结构中注册的依赖属性,请使用该方法 AddOwner 。 当添加类不是从注册依赖属性的类型派生时,通常会使用此方法。 在调用中 AddOwner ,添加类可以为继承的依赖属性创建和分配特定于类型的元数据。 若要通过代码和标记成为属性系统中的完整参与者,添加类应实现以下公共成员:

  • 依赖属性标识符字段。 依赖属性标识符的值是调用的 AddOwner 返回值。 此字段应为 public static readonly 类型的 DependencyProperty字段。

  • 实现 getset 访问器的 CLR 包装器。 通过使用属性包装器,依赖属性的使用者可以获取或设置依赖属性值,就像任何其他 CLR 属性一样。 getset访问器通过DependencyObject.GetValueDependencyObject.SetValue调用,与基础属性系统进行交互,并将依赖属性标识符作为参数传入。 以注册自定义依赖属性时相同的方式实现包装器。 有关详细信息,请参阅 自定义依赖项属性

调用AddOwner的类在公开继承的依赖属性的对象模型方面,与定义新自定义依赖属性的类有相同的要求。 有关详细信息,请参阅 为依赖属性添加所有者类型

附加属性元数据

在 WPF 中,WPF 类型上大多数与 UI 相关的附加属性都作为依赖属性实现。 作为依赖属性实现的附加属性支持依赖属性概念,例如派生类可以重写的元数据。 附加属性的元数据通常与依赖属性没有什么不同。 可以在重写类的实例上重写继承的附加属性的默认值、属性更改回调和 WPF 框架属性。 有关详细信息,请参阅 附加属性元数据

注释

始终使用 RegisterAttached 来注册在元数据中指定 Inherits 的属性。 尽管属性值继承可能适用于非附加依赖属性,但未定义通过运行时树中的某些对象对象划分的非附加属性的值继承行为。 该 Inherits 属性与非附加属性无关。 有关详细信息,请参阅 RegisterAttached(String, Type, Type, PropertyMetadata)和备注 Inherits部分。

将一个类作为附属属性的所有者添加

若要从另一个类继承附加属性,并在您的类中将其公开为非附加的依赖属性:

  • 您可以调用 AddOwner 将您的类添加为相关依赖属性的所有者。

  • AddOwner调用的返回值分配给public static readonly字段,以用作依赖属性标识符。

  • 定义 CLR 包装器,该包装器将属性添加为类成员,并支持非附加属性用法。

另请参阅