WPF 中的 DependencyProperty 是实现数据绑定、样式、模板、动画等功能的基础,需通过静态注册、GetValue/SetValue 访问,并推荐提供同名包装属性;其创建分三步:定义 static readonly 字段、调用 Register 注册、添加包装属性;XAML 中可直接使用如 ;支持元数据(默认值、变更/强制回调)及 FrameworkPropertyMetadata 标记;仅限 DependencyObject 派生类使用,禁在构造函数中 SetValue 初始化。

WPF 中的 DependencyProperty 是实现数据绑定、样式、模板、动画、属性继承等核心功能的基础。它不是普通 .NET 属性,而是一种由 WPF 属性系统管理的特殊属性,必须通过静态注册方式声明,并配合 GetValue / SetValue 方法访问。
依赖属性怎么创建
创建依赖属性需三步:定义静态只读字段、在类中注册、提供标准包装属性(可选但强烈推荐)。
- 使用
DependencyProperty.Register注册,传入属性名、类型、所属类类型、可选元数据(如默认值、回调) - 注册返回的
DependencyProperty对象必须赋给public static readonly字段 - 为方便 XAML 和 C# 代码调用,建议提供同名的 .NET 属性包装器,内部调用
GetValue/SetValue
示例(在自定义控件 MyButton 中定义 CornerRadius 依赖属性):
public class MyButton : Control
{
// 1. 静态只读字段
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register(
nameof(CornerRadius),
typeof(double),
typeof(MyButton),
new PropertyMetadata(0.0)); // 默认值为 0.0
// 2. 包装属性(非必需但实用)
public double CornerRadius
{
get => (double)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}}
依赖属性怎么在 XAML 中使用
XAML 中使用依赖属性和普通属性写法一致,只要该属性是公开的包装属性或直接支持属性语法即可。
- 直接赋值:
- 绑定表达式:
- 动画目标:
- 样式 Setter:
注意:XAML 实际解析时,会通过反射找到 CornerRadiusProperty 字段并调用 SetValue,所以包装属性名必须与字段名(去掉 Property 后缀)严格匹配。
依赖属性的常用元数据与回调
注册时传入的 PropertyMetadata 可指定默认值、属性变更回调、属性值强制回调等,增强行为控制。
-
DefaultValue:影响属性未显式设置时的表现(如不触发样式触发器) -
PropertyChangedCallback:值改变后触发,接收 old/new 值,适合同步更新内部状态 -
CoerceValueCallback:在值被设入前“修正”它(例如限制范围、对齐步长) -
FrameworkPropertyMetadata:额外标记(如是否参与布局、是否可继承、是否影响渲染),常用于 UI 控件
例如限制 CornerRadius 不小于 0:
new FrameworkPropertyMetadata(
0.0,
OnCornerRadiusChanged,
CoerceCornerRadius)
private static object CoerceCornerRadius(DependencyObject d, object baseValue)
=> Math.Max(0.0, (double)baseValue);
private static void OnCornerRadiusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var btn = (MyButton)d;
btn.InvalidateVisual(); // 触发重绘
}
依赖属性的注意事项
依赖属性能力强大,但也有明确约束和易错点:
- 只能在
DependencyObject派生类中定义(如UIElement、FrameworkElement、Control) - 不能在构造函数中通过
SetValue初始化(应改用元数据默认值或在OnInitialized中设) - 不要在包装属性的 getter/setter 中加复杂逻辑或异常抛出——它们可能被频繁调用,且部分场景(如模板实例化)会绕过包装器
- 多个依赖属性共用一个回调方法时,务必用
d as XXX安全转换,避免类型错误
基本上就这些。掌握注册模式、理解包装器作用、善用元数据回调,就能稳妥地扩展 WPF 控件行为。










