# 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)
);参数说明
使用方式
设置值:
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. 小结
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)
发生的是:
使用 dp(DependencyProperty 实例)作为 key
在 当前 DependencyObject 实例内部 查找
查找顺序:
动画值
本地值
样式 / 触发器
继承值
默认值
返回最终计算值(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 永远只操作对象自身。