WPF_DependencyObject

decade
16
2026-01-15
# WPF 属性体系详解:ap(附加属性)、dp(依赖属性)、do(DependencyObject)

## 1. 依赖对象(do - DependencyObject)

### 定义
在WPF中,所有支持依赖属性的基类都继承自 `DependencyObject`。

### 作用
提供机制存储依赖属性(`DependencyProperty`)和值。

### 示例代码
```csharp
public class TextBlockHelper
{
    // 示例:通过Get/Set操作依赖属性
    public static string GetTitle(DependencyObject obj) => (string)obj.GetValue(TitleProperty);
    public static void SetTitle(DependencyObject obj, string value) => obj.SetValue(TitleProperty, value);
}

用途

  • 为对象动态存储和获取绑定属性

  • 响应属性变换

2. 依赖属性(dp - DependencyProperty)

定义

一种特殊的属性,用于支持绑定、样式、动画等WPF特性。

注册方式

通过 DependencyProperty.Register 静态方法注册。

示例代码

csharp

public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(
    "Title",
    typeof(string),
    typeof(TextBlockHelper),
    new PropertyMetadata(null, callback)
);

参数说明

参数

说明

"Title"

依赖属性的名称

typeof(string)

属性类型

typeof(TextBlockHelper)

所属的注册类

new PropertyMetadata(null, callback)

默认值,及值变化的回调函数

使用方式

  • 设置值

    csharp

    TextBlockHelper.SetTitle(someObject, "示例标题");
  • 获取值

    csharp

    string title = TextBlockHelper.GetTitle(someObject);

3. 附加属性(ap - Attached Property)

定义

一种特殊的依赖属性,用于"附加"在其他控件上,为控件赋予额外的功能或状态。

注册方式

csharp

public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached(
    "Title",
    typeof(string),
    typeof(TextBlockHelper),
    new PropertyMetadata(null, callback)
);

Setter/Getter

csharp

public static string GetTitle(DependencyObject obj) => (string)obj.GetValue(TitleProperty);
public static void SetTitle(DependencyObject obj, string value) => obj.SetValue(TitleProperty, value);

用途

  • 可以在XAML中作为属性使用:

    xml

    <TextBlock local:TextBlockHelper.Title="显示的标题" />

重要特点

  • 可绑定:可以绑定到数据源

  • 用于:布局控制、行为扩展、样式等

4. 属性变化的回调(callback 方法)

定义

当绑定目标的依赖属性值发生变化时,callback会被调用,用于响应变化。

示例代码

csharp

private static void callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // 实现逻辑:如更新UI、触发动画等
    throw new NotImplementedException();
}

触发时机

  • 属性首次设置(初始化)

  • 绑定源变化

  • 通过代码调用 SetValue 修改

5. 结合示例

代码片段

csharp

public class TextBlockHelper
{
    public static string GetTitle(DependencyObject obj) => (string)obj.GetValue(TitleProperty);
    public static void SetTitle(DependencyObject obj, string value) => obj.SetValue(TitleProperty, value);
    
    public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached(
        "Title",
        typeof(string),
        typeof(TextBlockHelper),
        new PropertyMetadata(null, callback)
    );

    private static void callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        throw new NotImplementedException(); // 属性变更可能会用到的逻辑
    }
}

作用

  • 定义了一个附加属性 Title,可以附加到任何支持 DependencyObject 的控件

  • Title 的值发生变化,callback 会被调用,可以写具体逻辑(更新UI、触发动画等)

6. 小结

概念

作用

使用场景

DependencyObject (do)

能存储和管理依赖属性

所有支持依赖属性的对象基类

DependencyProperty (dp)

注册和定义具体属性

支持绑定、样式、动画等

附加属性 (ap)

扩展控件,使其他控件也能拥有自定义属性

设计灵活、复用性强的UI行为

7. 简单示意图

text

+--------------------------------------------------+
| DependencyObject (控件)                           |
| +----------------------------------------------+ |
| | DependencyProperty (Title)                   | |
| +----------------------------------------------+ |
|                                                  |
| (支持绑定/样式/动画)                            |
+--------------------------------------------------+

附加属性(ap):可以附加到任何 DependencyObject,扩展其功能

WPF 中 DependencyObject 与 DependencyProperty 的本质关系(笔记)

一句话总览
值存在对象上,DependencyProperty 只是 key。


一、核心概念速览(先有全局感)

1️⃣ DependencyProperty(DP)

  • 是什么
    一个 全局唯一的属性定义对象

  • 做什么
    描述「这是什么属性」

  • 不做什么
    ❌ 不存任何具体值

👉 可以理解为:

属性的“身份证 + 规则说明书”


2️⃣ DependencyObject(DO)

  • 是什么
    WPF 属性系统中的“值宿主”

  • 做什么

    • 保存属性值

    • 计算最终值(动画 / 绑定 / 样式)

  • 关键能力来源
    GetValue / SetValue

👉 可以理解为:

真正存放属性值的对象实例


二、DependencyProperty.Register 到底做了什么?

调用代码

public static readonly DependencyProperty IsHighlightedProperty =
    DependencyProperty.Register(
        "IsHighlighted",
        typeof(bool),
        typeof(Human),
        new PropertyMetadata(false));

Register 的返回值

返回一个 DependencyProperty 对象实例

这个对象里包含:

  • 属性名(IsHighlighted)

  • 属性类型(bool)

  • OwnerType(Human)

  • 默认值

  • 回调信息

  • 全局唯一的 int 索引

📌 注意:没有任何属性值


三、全局注册表是干什么用的?

1️⃣ 本质

DependencyProperty 的全局注册中心(Registry)


2️⃣ 注册表中存的是什么?

Key(逻辑概念)

(OwnerType, PropertyName)

Value

DependencyProperty 实例

📌 Register 返回的对象 就是注册表中的 value 本身


3️⃣ 注册表的作用(非常重要)

  • 防止同一类型上重复注册同名 DP

  • XAML / Style / Trigger 中:

    • 从字符串 "IsHighlighted"

    • 找到对应的 DP 实例

👉 只负责“按名字找 DP”


❌ 注册表不负责什么

  • ❌ 不存属性值

  • ❌ 不参与 GetValue / SetValue


四、属性值到底存在哪里?

答案很明确:

属性值永远存在 DependencyObject 实例上

每个对象都有自己的一份。


五、EffectiveValueEntry 是什么?是不是哈希表?

❌ 不是 HashTable / Dictionary

✅ 实际是:

按 DP 全局索引组织的紧凑数组结构

特点:

  • 每个 DP 在注册时获得一个全局 int ID

  • DependencyObject 内部维护:

    • EffectiveValueEntry[]

  • 只存「这个对象真正用到的 DP」

  • 按索引排序,查找极快

👉 更像 稀疏数组(Sparse Array)


六、GetValue / SetValue 的完整执行流程

从这行代码开始

(bool)GetValue(IsHighlightedProperty)

1️⃣ 前置:DP 已注册

  • IsHighlightedProperty 是一个 DP 对象

  • 全局唯一

  • 已经存在于注册表中


2️⃣ GetValue 执行过程

GetValue(dp)

发生的是:

  1. 使用 dp(DependencyProperty 实例)作为 key

  2. 当前 DependencyObject 实例内部 查找

  3. 查找顺序:

    • 动画值

    • 本地值

    • 样式 / 触发器

    • 继承值

    • 默认值

  4. 返回最终计算值(Effective Value)

📌 整个过程不访问全局注册表


3️⃣ SetValue 执行过程

SetValue(IsHighlightedProperty, true)

等价于:

在当前对象内部:
   dp → true

七、什么时候才会用到全局注册表?

只有在“没有 DP 对象,只有名字”的时候

典型场景:

<Setter Property="IsHighlighted" Value="True"/>

执行流程:

"IsHighlighted" + TargetType
        ↓
全局注册表
        ↓
DependencyProperty 实例
        ↓
DependencyObject 内部存值

八、最安全、最不容易出错的理解版本(强烈推荐)

✅ 一句话版本

DependencyProperty 是全局唯一的属性定义对象;
全局注册表只负责按名字找到它;
DependencyObject 使用 DP 作为 key,
在自身内部保存和查找属性值。


✅ 极简心智模型

DependencyProperty  → 定义 / key
DependencyObject    → 存值 / 计算

九、为什么 WPF 要这样设计?

  • 高性能(60fps)

  • 支持动画 / 绑定 / 样式优先级

  • 内存可控

  • 支持属性继承和批量失效

👉 这是 UI 框架级的工程设计,不是语法糖


十、最终一句“不会再混”的总结

值在对象上,
DP 只是 key,
注册表只负责找 key,
GetValue / SetValue 永远只操作对象自身。