3.WPF - 依赖属性

WPF - 依赖属性

简述

在 WPF 应用程序中,依赖属性是扩展 CLR 属性的特定类型的属性。它利用了 WPF 属性系统中可用的特定功能。

定义依赖属性的类必须继承自 DependencyObject 类。 XAML 中使用的许多 UI 控件类都派生自 DependencyObject 类,它们支持依赖属性,例如Button 类支持 IsMouseOver 依赖属性。

以下 XAML 代码创建一个带有一些属性的按钮。

csharp 复制代码
<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() 类似的功能。它用于指定采用对象类型的属性时,例如

csharp 复制代码
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. 初始化阶段

    用户控件加载 构造函数 InitializeComponent XAML解析器 内存 new UserControl1() 调用 加载UserControl1.xaml 创建UI元素树 返回控件实例 用户控件加载 构造函数 InitializeComponent XAML解析器 内存

  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. 回调模式设计

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

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

  3. XAML依赖

    • tbTest必须在XAML中正确定义:
    xaml 复制代码
    <UserControl ...>
        <Grid>
            <TextBlock x:Name="tbTest" />
        </Grid>
    </UserControl>
    • 如果名称不匹配,运行时会抛出NullReferenceException
  4. 空值处理

    csharp 复制代码
    e.NewValue?.ToString() ?? string.Empty;

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

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

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

相关推荐
mudtools2 小时前
.NET操作Excel:高级格式设置
c#·.net·excel·wps
温启志c#3 小时前
winform c# 做的2个运控平台,通过修改表格 的方式,也可以通过语音识别的交互方式,更加智能。
开发语言·c#
Dream achiever5 小时前
11.WPF 的命令处理事件--参数介绍
开发语言·c#·wpf
布伦鸽5 小时前
C# WPF DataGrid 数据绑定时的单元格编辑类型模板
开发语言·c#·wpf
Dream achiever5 小时前
5.WPF控件---ComboBox
wpf
入目星河滚烫6 小时前
Unity避坑——继承了MonoBehaviour的对象不能通过new来创建
unity·c#·游戏引擎
大飞pkz6 小时前
【设计模式】组合模式
开发语言·设计模式·c#·组合模式
极客智造6 小时前
FAQ: 如何在 WPF 项目中强制指定统一输出目录并确保 VS 调试正常?
wpf
Dream achiever6 小时前
6.WPF控件---Label
开发语言·wpf