3.WPF - 依赖属性 - 实践

WPF - 依赖属性
简述
在 WPF 应用程序中,依赖属性是扩展 CLR 属性的特定类型的属性。它利用了 WPF 属性系统中可用的特定功能。
定义依赖属性的类必须继承自 DependencyObject 类。 XAML 中使用的许多 UI 控件类都派生自 DependencyObject 类,它们支持依赖属性,例如Button 类支持 IsMouseOver 依赖属性。
以下 XAML 代码创建一个带有一些属性的按钮。

<Window x:Class = "WPFDependencyProperty.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local = "clr-namespace:WPFDependencyProperty"
Title = "MainWindow" Height = "350" Width = "604">
<Grid>
  <Button  Height = "40" Width = "175" Margin = "10" Content = "Dependency Property">
    <Button.Style>
      <Style TargetType = "{x:Type Button}">
        <Style.Triggers>
          <Trigger Property = "IsMouseOver" Value = "True">
            <Setter Property = "Foreground" Value = "Red" />
              </Trigger>
                </Style.Triggers>
                  </Style>
                    </Button.Style>
                      </Button>
                        </Grid>
                          </Window>

XAML 中的 x:Type 标记扩展具有与 C# 中的 typeof() 类似的功能。它用于指定采用对象类型的属性时,例如

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;                // WPF基础命名空间(包含DependencyObject等)
using System.Windows.Controls;       // 用户控件相关类(UserControl)
using System.Windows.Data;           // 数据绑定支持
using System.Windows.Documents;      // 富文本支持
using System.Windows.Input;          // 输入处理(键盘、鼠标)
using System.Windows.Media;          // 图形和渲染(画笔、画刷)
using System.Windows.Media.Imaging;  // 图像处理
using System.Windows.Navigation;     // 导航支持
using System.Windows.Shapes;         // 基本形状(矩形、椭圆等)
namespace WpfApplication3
{
/// <summary>
  /// 自定义用户控件类,继承自UserControl
  /// 演示依赖属性的实现和使用模式
/// </summary>
public partial class UserControl1 : UserControl  // partial类:XAML文件将定义另一部分
{
/// <summary>
  /// 构造函数:初始化控件
/// </summary>
public UserControl1()
{
// 调用InitializeComponent()方法:
// 1. 加载关联的XAML文件(UserControl1.xaml)
// 2. 实例化XAML中定义的UI元素
// 3. 注册事件处理程序
// 4. 初始化x:Name标识的控件(如tbTest)
InitializeComponent();
}
/// <summary>
  /// 依赖属性标识符(静态只读字段)
  /// 命名惯例:属性名 + "Property"后缀
/// </summary>
public static readonly DependencyProperty SetTextProperty =
// 使用DependencyProperty.Register方法注册依赖属性
DependencyProperty.Register(
"SetText",                  // 属性名称(字符串)
typeof(string),             // 属性类型(System.String)
typeof(UserControl1),       // 所有者类型(声明该属性的类)
new PropertyMetadata(       // 属性元数据配置
"",                     // 默认值(空字符串)
new PropertyChangedCallback(OnSetTextChanged) // 属性变更回调方法
)
);
/// <summary>
  /// CLR属性包装器(公共接口)
  /// 提供对依赖属性的类型安全访问
/// </summary>
public string SetText
{
// 通过GetValue获取依赖属性当前值
// DependencyProperty.GetValue()是WPF属性系统的核心方法
get { return (string)GetValue(SetTextProperty); }
// 通过SetValue设置依赖属性值
// 注意:WPF数据绑定会直接调用此方法,绕过setter中的代码
set { SetValue(SetTextProperty, value); }
}
/// <summary>
  /// 静态属性变更回调方法
  /// 符合DependencyPropertyChangedCallback委托签名
/// </summary>
/// <param name="d">发生属性变更的DependencyObject实例(这里是UserControl1实例)</param>
/// <param name="e">包含新旧值等信息的变更参数</param>
private static void OnSetTextChanged(
DependencyObject d,                 // 依赖对象(控件实例)
DependencyPropertyChangedEventArgs e) // 变更事件参数
{
// 将通用DependencyObject转换为具体类型UserControl1
// 这是必要的,因为静态方法没有this引用
UserControl1 userControl = d as UserControl1;
// 空值检查:确保转换成功
if (userControl != null)
{
// 调用实例方法处理实际变更逻辑
userControl.OnSetTextChanged(e);
}
}
/// <summary>
  /// 实例级属性变更处理方法
  /// 执行实际的UI更新逻辑
/// </summary>
/// <param name="e">包含新旧值的变更参数</param>
private void OnSetTextChanged(DependencyPropertyChangedEventArgs e)
{
// 获取新值并转换为字符串:
// 1. e.NewValue 是Object类型,包含变更后的值
// 2. 使用?.操作符避免空引用异常
// 3. 使用??提供空值回退(空字符串)
string newValue = e.NewValue?.ToString() ?? string.Empty;
// 更新UI元素:
// tbTest 是在XAML中定义的TextBlock控件(通过x:Name="tbTest")
tbTest.Text = newValue;
}
}
}

代码执行流程详解:

  1. 初始化阶段

    用户控件加载构造函数InitializeComponentXAML解析器内存new UserControl1()调用加载UserControl1.xaml创建UI元素树返回控件实例用户控件加载构造函数InitializeComponentXAML解析器内存
  2. 属性变更流程

    外部设置属性CLR包装器依赖属性系统静态回调实例方法TextBlock界面SetText = "Hello"SetValue(SetTextProperty, "Hello")OnSetTextChanged(d, e)userControl.OnSetTextChanged(e)tbTest.Text = "Hello"更新显示文本外部设置属性CLR包装器依赖属性系统静态回调实例方法TextBlock界面

关键设计要点解析:

  1. 依赖属性声明

    • DependencyProperty.Register 是WPF属性系统的核心注册方法
    • 元数据中的PropertyChangedCallback指定值变更时的通知机制
    • 静态字段命名SetTextProperty是WPF的标准约定
  2. 回调模式设计

    // 静态方法(框架要求)
    private static void OnSetTextChanged(DependencyObject d, ...)
    {
    // 转换对象类型
    var control = (UserControl1)d;
    // 调用实例方法
    control.InstanceOnTextChanged(...);
    }
    // 实例方法(业务逻辑)
    private void InstanceOnTextChanged(...)
    {
    // 实际UI更新代码
    }

    这种模式是WPF的标准实践,兼顾了框架要求(静态方法)和面向对象设计(实例方法)

  3. XAML依赖

    • tbTest必须在XAML中正确定义:
    
        
            
        
    
    • 如果名称不匹配,运行时会抛出NullReferenceException
  4. 空值处理

    e.NewValue?.ToString() ?? string.Empty;

    使用空条件运算符和空合并运算符确保:

    • e.NewValue为null时不引发异常
    • ToString()返回null时使用空字符串代替

此实现完整展示了WPF自定义控件的标准模式,是理解依赖属性工作机制的经典范例。

posted on 2025-09-27 16:46  slgkaifa  阅读(13)  评论(0)    收藏  举报

导航