当前位置: 首页 > news >正文

WPF依赖属性

WPF依赖属性

 依赖属性基础概念

  • 什么是依赖属性?
  • 依赖属性是WPF属性系统的核心扩展,它扩展了传统的.NET属性,提供了数据绑定、样式、动画、资源引用等高级功能支持。

  • 为什么WPF需要依赖属性?

    依赖属性的设计是为了解决WPF复杂应用场景下的需求:

  • 内存效率:一个典型的WPF界面有成千上万的属性(如Width, Height, Background)。如果每个属性都用一个字段存储,内存消耗巨大。依赖属性只在被显式设置过值时才存储,否则使用默认值,这大大节省了内存。

  • 动态值解析:一个依赖属性的最终值可能由多个提供者决定(本地值、样式、模板、动画、继承值等)。WPF属性系统会按照一个确定的优先级来计算出最终的、有效的属性值。这是普通属性无法做到的。

  • 声明式编程:WPF的核心是XAML这种声明式语言。依赖属性完美地支持了通过XAML设置的样式、触发器、数据绑定和动画,使得UI逻辑与业务逻辑分离。

代码示例

public class MyWpfControl : DependencyObject
{// 1. 声明并注册一个静态的DependencyProperty标识符public static readonly DependencyProperty MyValueProperty =DependencyProperty.Register("MyValue", typeof(int), typeof(MyWpfControl), new PropertyMetadata(0, OnMyValueChanged)); // 设置默认值和变更回调// 2. 提供CLR包装器,方便外部使用(内部调用GetValue/SetValue)public int MyValue{get { return (int)GetValue(MyValueProperty); }set { SetValue(MyValueProperty, value); }}// 3. 属性变更回调方法(可选)private static void OnMyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){MyWpfControl control = d as MyWpfControl;int newValue = (int)e.NewValue;int oldValue = (int)e.OldValue;// 当MyValue改变时,可以在这里执行一些逻辑Console.WriteLine($"值从 {oldValue} 变为 {newValue}");}
}

普通CLR属性与依赖属性详细对比

特性普通CLR属性依赖属性
底层实现私有字段 + get/set访问器。在静态DependencyProperty中注册,并使用DependencyObject.GetValueSetValue方法。
存储机制值存储在类的私有字段中。值由WPF属性系统在全局的、高效的键值对集合中统一管理。对象本身不直接存储值。
默认值需要在构造函数中初始化,或直接赋予初始值。在注册依赖属性时直接指定,非常方便。
功能支持功能有限,仅支持简单的获取和设置。功能强大,是WPF许多核心功能的基石。
变更通知需要手动实现INotifyPropertyChanged接口。内置变更通知。任何时候属性值改变,WPF属性系统都会自动通知所有相关方。
数据绑定可以作为绑定源(需实现INotifyPropertyChanged),但作为绑定目标能力较弱。一等公民。完美支持作为绑定源绑定目标,并且是双向绑定的前提。
样式与动画不支持。可以被样式(Setter)、触发器(Trigger)和动画(Animation)所设置和改变。
属性值继承不支持。支持。例如,在Window上设置FontSize,其内部所有控件(除非自己重写)都会继承这个值。
元数据覆盖不支持。支持。可以在派生类中覆盖依赖属性的元数据(如默认值、属性变更回调)。


最常用的快捷方式

Visual Studio提供了内置的代码片段来快速生成依赖属性。

基本使用方法:

  1. 在类中输入 propdp

  2. 按 Tab 键两次

  3. 按照提示修改参数

示例:

public class MyControl : Control
{// 输入 "propdp" 然后按两次 Tab 键,会自动生成以下代码:public int MyProperty{get { return (int)GetValue(MyPropertyProperty); }set { SetValue(MyPropertyProperty, value); }}// Using a DependencyProperty as the backing store for MyProperty.// This enables animation, styling, binding, etc...public static readonly DependencyProperty MyPropertyProperty =DependencyProperty.Register("MyProperty", typeof(int), typeof(MyControl), new PropertyMetadata(0));
}

然后你只需要修改:

  • 属性名(如 MyProperty → TextValue

  • 类型(如 int → string

  • 默认值(如 0 → "" 或 null


扩展的代码片段模板

除了 propdp,还有一些社区提供的扩展片段:

propa - 附加属性

// 输入 "propa" + Tab + Tab
public static readonly DependencyProperty IsSpinningProperty =DependencyProperty.RegisterAttached("IsSpinning", typeof(bool), typeof(MyAttachedProperties), new PropertyMetadata(false));public static void SetIsSpinning(UIElement element, bool value)
{element.SetValue(IsSpinningProperty, value);
}public static bool GetIsSpinning(UIElement element)
{return (bool)element.GetValue(IsSpinningProperty);
}

propdpco - 带变更通知的依赖属性

一些扩展插件提供这个片段,自动生成带有回调的依赖属性。


自定义代码模板

你可以创建自己的代码片段或者使用以下"最小化"的模板:

基础模板(复制粘贴用)

#region MyProperty DependencyProperty
public string MyProperty
{get { return (string)GetValue(MyPropertyProperty); }set { SetValue(MyPropertyProperty, value); }
}public static readonly DependencyProperty MyPropertyProperty =DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl), new PropertyMetadata(""));
#endregion

带变更回调的模板

#region Value DependencyProperty
public double Value
{get { return (double)GetValue(ValueProperty); }set { SetValue(ValueProperty, value); }
}public static readonly DependencyProperty ValueProperty =DependencyProperty.Register(nameof(Value), typeof(double), typeof(MyControl), new PropertyMetadata(0.0, OnValueChanged));private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{var control = (MyControl)d;control.OnValueChanged((double)e.OldValue, (double)e.NewValue);
}private void OnValueChanged(double oldValue, double newValue)
{// 实例方法中处理变更
}
#endregion

常用依赖属性模式

只读依赖属性

private static readonly DependencyPropertyKey IsReadyPropertyKey =DependencyProperty.RegisterReadOnly(nameof(IsReady), typeof(bool), typeof(MyControl), new PropertyMetadata(false));public static readonly DependencyProperty IsReadyProperty = IsReadyPropertyKey.DependencyProperty;public bool IsReady
{get { return (bool)GetValue(IsReadyProperty); }private set { SetValue(IsReadyPropertyKey, value); }
}

枚举类型的依赖属性

public StatusType Status
{get { return (StatusType)GetValue(StatusProperty); }set { SetValue(StatusProperty, value); }
}public static readonly DependencyProperty StatusProperty =DependencyProperty.Register(nameof(Status), typeof(StatusType), typeof(MyControl), new PropertyMetadata(StatusType.None));


实用技巧

使用 nameof 操作符
  • 避免硬编码字符串,使用 nameof(MyProperty) 而不是 "MyProperty",这样在重命名属性时会自动更新。
区域包装
  • 使用 #region 将相关的依赖属性包装起来,让代码更整洁。

完整的最佳实践示例

public class CustomTextBox : Control
{#region WatermarkText DependencyPropertypublic string WatermarkText{get { return (string)GetValue(WatermarkTextProperty); }set { SetValue(WatermarkTextProperty, value); }}public static readonly DependencyProperty WatermarkTextProperty =DependencyProperty.Register(nameof(WatermarkText), typeof(string), typeof(CustomTextBox), new PropertyMetadata("请输入文本"));#endregion#region MaxLength DependencyPropertypublic int MaxLength{get { return (int)GetValue(MaxLengthProperty); }set { SetValue(MaxLengthProperty, value); }}public static readonly DependencyProperty MaxLengthProperty =DependencyProperty.Register(nameof(MaxLength), typeof(int), typeof(CustomTextBox), new PropertyMetadata(100, OnMaxLengthChanged));private static void OnMaxLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){var control = (CustomTextBox)d;control.ValidateMaxLength();}#endregion
}

记住核心快捷键:propdp + Tab + Tab,这是最快速的依赖属性创建方式!

http://www.dtcms.com/a/464839.html

相关文章:

  • 数据可视化 ECharts
  • javascript 性能优化实例一则
  • mapbox基础,使用矢量切片服务(pbf)加载line线图层
  • LLVM(Low Level Virtual Machine)介绍
  • Docker 一键部署指南:GitLab、Nacos、Redis、MySQL 与 MinIO 全解析
  • HDLBit 个人记录
  • 基于Jetson+FPGA+GMSL+AI的自动驾驶数据采集解决方案
  • 0006.C#学习笔记3-- HTML和CSS
  • 基于X86+FPGA+GPU的自动驾驶数据回灌测试解决方案
  • 在JavaScript / HTML中,Chrome报错Refused to execute inline script
  • 自动驾驶的“虚拟驾校”如何炼成?
  • 自动驾驶传感器数据录制过程中的五大系统性挑战
  • 学校网站建设开题报告书wordpress 发布说说
  • 企业如何减少由于数据不一致带来的运营成本?
  • 安卓开发APP应用程序和苹果iOS开发APP应用程序有什么区别?
  • Mac 上用 Homebrew 安装 JDK 8(适配 zsh 终端)完整教程
  • 利用小偷程序做网站企业网站开发建设
  • K8S基本命令操作
  • 【kubernetes/k8s源码分析】kube-controller-manager之node controller源码分析
  • SMOTE 算法详解:解决不平衡数据问题的有效工具
  • HGDB集群(安全版)repmgr手动切换主备库
  • 三维GIS数据转换指南:SHAPE文件到3DTiles的高效实现方案
  • K8S(三)—— 基于kubeadm 1.20版本部署Kubernetes集群与Harbor私有仓库实战
  • 宁波外贸网站制作公司手机网站建设哪家公司好
  • 【C语言实战(8)】C语言循环结构(do-while):解锁编程新境界
  • 面向Qt/C++开发工程师的Ai提示词(附Trae示例)
  • sqlite 使用: 01-源码编译与使用
  • Django视图进阶:快捷函数、装饰器与请求响应
  • 企业营销网站的建设网站开发响应式
  • 掌握DMA基于GD32F407VE的天空星的配置