CH04_依赖项属性
第4章:依赖项属性
本章目标
- 理解依赖项属性
- 理解属性验证
依赖项属性
属性与事件是.NET抽象模型的核心部分。WPF使用了更高级的依赖项属性(Dependency Property)功能来替换原来.NET的属性,实现了更高效率的保存机制,还添加了附加功能,如属性变更通知以及强制回调、属性值继承(在逻辑树中向下传播默认属性值的能力)以及属性有效性验证等。同时,依赖项属性也是WPF许多重要功能的基础,包括动画、数据绑定以及样式。
使用依赖项属性包括三个部分,定义依赖项属性;注册依赖项属性以及添加属性包装器。
定义依赖项属性
定义依赖项属性,使用三个修饰词,public、static、readonly。数据类型为DependecyProperty,而每一个依赖项属性都会有一个去掉“Property”的CLR属性和他对应,而我们在xaml中访问的都是CLR属性。
根据约定,定义依赖项属性的字段的名称是在普通属性的末尾处加上单词"Property"。根据这种命名方式,可以从实际属性的名称中区分出依赖项属性的定义。字段的定义使用了 readoly 关键字,这意味着只能在 FrameworkElement 类的静态构造函数中队旗进行设置。
例如,FrameworkElement 类定义了 Margin 属性,在 FrameworkElement 类中需要使用类似下面的代码来定义Margin 属性:

注册依赖项属性
定义 DependencyProperty对象只是第一步而已。为了使用依赖项属性,还需要使用 WPF注册创建的依赖项属性。这一步骤需要在任何使用属性的代码之前完成,因此必须在与其关联的类的静态构造函数中进行。
WPF 确保 DependencyProperty 对象不能被直接实例化,因为 DependencyProperty 类没有公有的构造函数。相反,只能使用静态的 DependencyProperty Register0方法创建 DependencyProperty实例。WPF 还确保在创建 DependencyProperty 对象后不能改变该对象,因为所有DependencyProperty 成员都是只读的。它们的值必须作为 Register0方法的参数来提供。
下面的代码显示了如何创建 DependencyProperty 对象。在此,FrameworkElement类使用静态构造函数来初始化 MarginProperty:

添加属性包装器
创建依赖项属性的最后一个步骤是使用传统的.NET 属性封装WPF依赖项属性。但典型的过程是检索或设置某个私有字段的值,而WPF属性的属性过程是使用在 DependencyObject 基类中定义的 GetValue()和 SetValue()方法。

共享的依赖项属性
尽管一些类具有不同的继承层次,但他们会共享同一依赖项属性。例如,TextBlock.FontFamily属性和 Control.FontFamily 属性指向同一个静态的依赖项属性,该属性实际上实在 TextElement 类中定义的 TextElement.FontFamilyProperty 依赖项属性。TextElement 类的静态构造函数注册该属性,而 TextBlock 类和Control 类的静态构造函数知识通过调用 DependencyProperty.AddOwner()方法重用该属性:
TextBlock类:

Control类:

附加的依赖项属性
附加属性是一种依赖项属性,由WPF 属性系统管理。不同之处在于附加属性被应用到的类并非定义附加属性的那个类。例如,Grid 类定义了Row 和 Column 附加属性,这两个属性被用于设置Grid 面板包含的元素,以指明这些元素应被放到哪个单元格之中。类似的,DockPancel 类定义了 Dock 附加属性,而 Canvas 类定义了Left、Right、Top和Bottom 附加属性。
为了定义附加属性,需要使用 RegisterAttached() 方法,而不是使用 Register() 方法.下面列举了一个注册Grid.Row 属性的例子:

当创建附加属性时,不必定义.NET 属性封装器。这是因为附加属性可以被用于任何依赖对象。例如,Grid.Row 属性可能被用于 Grid 对象(如果在Grid 控件中嵌套了另一个Grid 控件),也可能被用于其他元素。实际上,Grid.Row 属性甚至可以被用于并不位于 Grid控件中的元素------- 甚至在元素树中根本就不存在 Grid 对象。
不是使用 .NET 属性封装器,反而附加属性需要调佣两个静态方法来设置和获取属性值,这两个方法使用为人熟知的 SetValue() 和 GetValue() 方法(继承自 DependencyObject 类)。这两个静态方法应当命名为 SetPropertyName() 和 GetPropertyName().下面是实现 Grid.Row 附加属性的静态方法:

下面的示例使用代码将元素放到Grid 控件中第一行:
Grid.SetRow(txtElement,0)
也可直接调用 SetValue() 或 GetValue() 方法,从而绕过这两个静态方法:
txtElement.SetValue(Grid.RowProperty,0)
属性验证
在定义任何类型的属性时,都需要面对错误设置属性的可能性。对于传统的.NET属性,可尝试在属性设置器中捕获这类问题。但对于依赖项属性而言,这种方法不合适,因为可能通过WPF 属性系统使用 SetValueO方法直接设置属性。
作为代替,WPF 提供了两种方法来阻止非法值:
- ValidateValueCallback:该回调函数可接受或拒绝新值。通常,该回调函数用于捕获违反属性约束的明显错误。可作为 DependencyProperty.Register()方法的一个参数提供该回调函数。
- CoerceValueCallback:该回调函数可将新值修改为更能被接受的值。该回调函数通常用于处理为相同对象设置的依赖项属性值相互冲突的问题。这些值本身可能是合法的,但当同时应用时它们是不相容的。为了使用这个回调函数,当创建 Framework-PropertyMetadata 对象时(然后该对象将被传递到 DependencyProperty.Register()方法),作为构造函数的一个参数提供该回调函数。
下面是当应用程序试图设置依赖项属性时,所有这些内容的作用过程:
(1) 首先,CoerceValueCallback 方法有机会修改提供的值(通常,使提供的值和其他属性相容),或者返回 DependencyProperty.UnsetValue,这会完全拒绝修改。
(2) 接下来激活 Validate ValueCallback 方法。该方法返回 true 以接受一个值作为合法值,或者返回 false拒纯值。与 Coerce ValueCallback 方法不同,Validate ValueCallback 方法不能访问设置属性的实际对象,这意味着您不能检查其他属性值。
(3) 最后,如果前两个阶段都获得成功,就会触发 PropertyChangedCallback 方法。此时,如果希望为其他类提供通知,可以引发更改事件。
验证回调
DependencyProperty.Register() 方法接受可选的验证回调函数:
MarginProperty = DependencyProperty.Register(
"Margin",
typeof(Thickness),
_typeofThis,
new FrameworkPropertyMetadata(default(Thickness),FrameworkPropertyMetadataOptions.AffectsMeasure), IsMarginValid);
private static bool IsMarginValid(object value)
{return ((Thickness)value).IsValid(allowNegative: true, allowNaN: false, allowPositiveInfinity: true, allowNegativeInfinity: false);
}
强制回调
通过 FrameworkPropertyMetadata 对象使用 CoerceValueCallback 回调函数。
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
metadata.CoerceValueCallback = new CoerceValueCallback(CoerceMaximum);DependencyProperty.Register("Maximum",typeof(double),typeof(RangeBase),metadata);
可以通过CoerceValueCallback 回调函数处理相互关联的属性。例如,ScrollBar 控件提供了 Maximum、Minimum 和 Value 属性,这些属性都继承自 RangeBase 类。保持对这些属性进行调整的一种方法是使用属性强制。例如,当设置Maximum 属性时,必须使用强制以确保不能小于 Minimum 属性的值:

当设置 Value 属性时,会发送类似的强制过程。对 Value 属性进行强制,确保不会超出由 Minimum 和Maximum 属性定义的范围,使用下面的代码:

Minimum 属性根本不使用值强制。相反,一旦值发生变化,就触发PropertyChangedCallback,然后通过手动触发 Maximum 和 Value 属性的强制过程,使它们适应 Minimum 属性值的变化:

类似的,一旦设置或强制 Maximum 属性的值,那么也会手动强制 Value 属性以适应 Maximum 属性值的变化:

如果设置的值相互冲突,最终结果是 Minimum 属性具有优先权,其次是 Maximum 属性(并且可能会被 Minimum 属性强制),最后是 Value 属性(并且可能会被 Maximum 和 Minimum 属性强制)。
本章小结
本章深入分析了 WPF 依赖项属性。首先介绍如何定义和注册依赖项属性,接下类介绍了如何将它们插入到其他 WPF 服务中,以及它们如何支持验证和强制。
课后作业
无
相关文章:
CH04_依赖项属性
第4章:依赖项属性 本章目标 理解依赖项属性理解属性验证 依赖项属性 属性与事件是.NET抽象模型的核心部分。WPF使用了更高级的依赖项属性(Dependency Property)功能来替换原来.NET的属性,实现了更高效率的保存机制…...
CentOS 7开启SSH连接
1. 安装openssh-server 1.1 检查是否安装openssh-server服务 yum list installed | grep openssh-server如果有显示内容,则已安装跳过安装步骤,否则进行第2步 1.2 安装openssh-server yum install openssh-server2. 开启SSH 22监听端口 2.1 打开ssh…...
代理伺服器分類詳解
代理伺服器的主要分類 代理伺服器可以根據不同的標準進行分類。以下是幾種常見的分類方式: 按協議分類按匿名性分類按使用場景分類 1. 按協議分類 根據支持的協議類型,代理伺服器可以分為以下幾類: HTTP代理:專門用於處理HTT…...
计数,桶与基数排序
目录 一. 计数排序 概念 步骤思路如下 实现代码如下 时间复杂度与空间复杂度 1. 时间复杂度 2. 空间复杂度 计数排序的特点 二. 桶排序 概念 步骤思路如下 实现代码如下 时间复杂度与空间复杂度 1. 时间复杂度 2. 空间复杂度 桶排序的特点 三. 基数排序 概念 步…...
unity渲染人物模型透明度问题
问题1:有独立的手和衣服的模型,但最终只渲染出来半透明衣服 问题2:透明度贴图是正确的但显示却不正确 这上面两个模型的问题都是因为人物模型是一个完整的,为啥有些地方可以正常显示,有些地方透明度却有问题。 其中…...
CH03_布局
第3章:布局 本章目标 理解布局的原则理解布局的过程理解布局的容器掌握各类布局容器的运用 理解 WPF 中的布局 WPF 布局原则 WPF 窗口只能包含单个元素。为在WPF 窗口中放置多个元素并创建更贴近实用的用户男面,需要在窗口上放置一个容器&#x…...
【Oracle】Oracle中的merge into
目录 解释使用场景语法示例案例一案例二 MERGE INTO的优缺点优点:缺点: 注意事项附:Oracle中的MERGE INTO实现的效果,如果改为用MySQL应该怎么实现注意 解释 在Oracle数据库中,MERGE INTO是一种用于对表进行合并&…...
【论文阅读笔记】In Search of an Understandable Consensus Algorithm (Extended Version)
1 介绍 分布式一致性共识算法指的是在分布式系统中,使得所有节点对同一份数据的认知能够达成共识的算法。且算法允许所有节点像一个整体一样工作,即使其中一些节点出现故障也能够继续工作。之前的大部分一致性算法实现都是基于Paxos,但Paxos…...
CentOS 7 网络配置
如想了解请查看 虚拟机安装CentOS7 第一步:查看虚拟机网络编辑器、查看NAT设置 (子网ID,网关IP) 第二步:配置VMnet8 IP与DNS 注意事项:子网掩码与默认网关与 第一步 保持一致 第三步:网络配置…...
2024 React 和 Vue 的生态工具
react Vue...
AI学习指南机器学习篇-t-SNE模型应用与Python实践
AI学习指南机器学习篇-t-SNE模型应用与Python实践 在机器学习领域,数据的可视化是非常重要的,因为它可以帮助我们更好地理解数据的结构和特征。而t-SNE(t-distributed Stochastic Neighbor Embedding)是一种非常强大的降维和可视…...
小试牛刀-Telebot区块链游戏机器人
目录 1.编写目的 2.实现功能 2.1 Wallet功能 2.2 游戏功能 2.3 提出功能 2.4 辅助功能 3.功能实现详解 3.1 wallet功能 3.2 游戏功能 3.3 提出功能 3.4 辅助功能 4.测试视频 Welcome to Code Blocks blog 本篇文章主要介绍了 [Telebot区块链游戏机器人] ❤博主…...
使用github actions构建多平台electron应用
1. 创建electron项目 使用pnpm创建项目 pnpm create quick-start/electron 2. 修改electron-builder.yml文件 修改mac的target mac:target:- target: dmgarch: universal 3. 添加workflow 创建 .github/workflows/main.yml 文件 name: Build/release Electron appon:work…...
java通过pdf-box插件完成对pdf文件中图片/文字的替换
需要引入的Maven依赖: <!-- pdf替换图片 --><dependency><groupId>e-iceblue</groupId><artifactId>spire.pdf.free</artifactId><version>5.1.0</version></dependency> java代码: public AjaxResult replacepd…...
鸿蒙 next 5.0 版本页面跳转传参 接受参数 ,,接受的时候 要先定义接受参数的类型, 代码可以直接CV使用 [教程]
1, 先看效果 2, 先准备好两个页面 index 页面 传递参数 import router from ohos.routerEntry Component struct Index {Statelist: string[] [星期一, 星期二,星期三, 星期四,星期五]StateactiveIndex: number 0build() {Row() {Column({ space: 10 }) {ForEach(this.list,…...
【electron6】浏览器实时播放PCM数据
pcm介绍:PCM(Puls Code Modulation)全称脉码调制录音,PCM录音就是将声音的模拟信号表示成0,1标识的数字信号,未经任何编码和压缩处理,所以可以认为PCM是未经压缩的音频原始格式。PCM格式文件中不包含头部信…...
嵌入式C/C++、FreeRTOS、STM32F407VGT6和TCP:智能家居安防系统的全流程介绍(代码示例)
1. 项目概述 随着物联网技术的快速发展,智能家居安防系统越来越受到人们的重视。本文介绍了一种基于STM32单片机的嵌入式安防中控系统的设计与实现方案。该系统集成了多种传感器,实现了实时监控、报警和远程控制等功能,为用户提供了一个安全、可靠的家居安防解决方案。 1.1 系…...
【Django】django自带后台管理系统样式错乱,uwsgi启动css格式消失的问题
正常情况: ERROR:(css、js文件加载失败) 问题:CSS加载的样式没有了,原因:使用了django自带的admin,在使用 python manage.py runserver启动 的时候,可以加载到admin的文…...
解决npm install(‘proxy‘ config is set properly. See: ‘npm help config‘)失败问题
摘要 重装电脑系统后,使用npm install初始化项目依赖失败了,错误提示:‘proxy’ config is set properly…,具体的错误提示如下图所示: 解决方案 经过报错信息查询解决办法,最终找到了两个比较好的方案&a…...
汽车及零部件研发项目管理系统:一汽东机工选择奥博思 PowerProject 提升研发项目管理效率
在汽车行业中,汽车零部件的研发和生产是一个关键的环节。随着汽车市场的不断扩大和消费者需求的不断增加,汽车零部件项目管理的重要性日益凸显。通过有效的项目管理方法及利用先进的数字项目管理系统,可以大幅提高项目的成功率和顺利度&#…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
