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

WPF|依赖属性SetCurrentValue方法不会使绑定失效, SetValue方法会使绑定失效?是真的吗?

引言

最近因为一个触发器设置的结果总是不起效果的原因,进一步去了解[依赖属性的优先级](Dependency property value precedence - WPF .NET | Microsoft Learn)。在学习这个的过程中发现对SetCurrentValue一直以来的谬误。

在WPF中依赖属性Dependency property的三个方法SetValue 、SetCurrentValue、ClearValue。

  1. SetCurrentValue 方法用于设置依赖属性的当前值,但不会覆盖该属性的值来源。这意味着,如果属性值是通过绑定、样式或触发器设置的,使用 SetCurrentValue 后,这些设置仍然有效。

然而这很容易让人以为SetValue 方法会使得数据绑定失效。就像先执行了ClearValue 一样。网上我看到的很多文章也这么说,这让我困惑了很久,但我实际操作下来,并非如此,实际上并不是。

为了验证这三个方法,我以设置按钮背景颜色为例,写了一个Demo。

其主要作用如下:

1. myButton绑定默认背景颜色的依赖属性
<Buttonx:Name="MyButton"Width="100"Height="50"Background="{Binding DefaultBackgroundColor,Mode=TwoWay,RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"Content="MyButton" />
 public static readonly DependencyProperty DefaultBackgroundColorProperty =DependencyProperty.Register("DefaultBackgroundColor",typeof(Brush),typeof(MainWindow),new PropertyMetadata(Brushes.Pink,OnDefaultBackgroundColorChanged));private static void OnDefaultBackgroundColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){MessageBox.Show("DefaultBackgroundColor changed");}public static readonly DependencyProperty DefaultForegroundColorProperty =DependencyProperty.Register("DefaultForegroundColor",typeof(Brush),typeof(MainWindow),new PropertyMetadata(Brushes.Gray,OnDefaultForegroundChanged));
2. 按钮1使用SetValue设置myButton的背景颜色属性,并判断绑定表达式是否为空
  private void SetValueChangeBackground_Click(object sender, RoutedEventArgs e){MyButton.SetValue(Button.BackgroundProperty, new SolidColorBrush(Colors.Green));IsBindingExpressionNull();}private void IsBindingExpressionNull()
{ if (MyButton.GetBindingExpression(Button.BackgroundProperty) == null){MessageBox.Show("BindingExpression is null");}
}
3. 按钮2使用SetCurrentValue设置myButton的背景颜色属性
 MyButton.SetCurrentValue(Button.BackgroundProperty, new SolidColorBrush(Colors.Orange));
IsBindingExpressionNull();
4. 按钮3 使用ClearValue 清楚背景颜色属性本地值
 // 清除本地值MyButton.ClearValue(Button.BackgroundProperty);
IsBindingExpressionNull();
5. 按钮4,修改依赖属性
DefaultBackgroundColor = new SolidColorBrush(Colors.LightGreen);
我使用.NET 8 做的Demo的现象如下:
  1. ClearValue执行后,绑定表达式为Null,也就是说ClearValue之后,绑定的数据表达式会被清空
  2. SetValue执行后,按钮颜色正常改变,绑定表达式不为Null。再次执行按钮4 修改依赖属性,按钮背景颜色也可以正常变化,也就是说SetValue之后数据表达式不会被清空,仍然有效。
  3. SetCurrentValue与第2点现象完全一致。

源码

通过查看源码,发现SetCurrentValueSetValue都执行方法SetValueCommon,只是入参coerceWithCurrentValue不同,// SetValue时为false 和SetCurrentValue时为true。

并且推测当SetValue的value入参等于DependencyProperty.UnsetValue时,应当会和ClearValueCommon执行相同的方法。

以下是测试代码,实际测试结果也是如此,此时绑定表达式为Null。

private void SetValueChangeBackgroundUnsetValue_Click(object sender, RoutedEventArgs e){MyButton.SetValue(Button.BackgroundProperty, DependencyProperty.UnsetValue);  //查看源码发现,UnsetValue时才会是清除本地值,并且 ClearValueIsBindingExpressionNull();}
SetValueCommon
        /// <summary>///     The common code shared by all variants of SetValue/// </summary>// Takes metadata from caller because most of them have already retrieved it//  for their own purposes, avoiding the duplicate GetMetadata call.private void SetValueCommon(DependencyProperty  dp,object              value,PropertyMetadata    metadata,bool                coerceWithDeferredReference,bool                coerceWithCurrentValue, // SetValue时为false 和SetCurrentValue时为trueOperationType       operationType,bool                isInternal){if (IsSealed){throw new InvalidOperationException(SR.Get(SRID.SetOnReadOnlyObjectNotAllowed, this));}Expression newExpr = null;DependencySource[] newSources = null;EntryIndex entryIndex = LookupEntry(dp.GlobalIndex);// Treat Unset as a Clearif( value == DependencyProperty.UnsetValue ){Debug.Assert(!coerceWithCurrentValue, "Don't call SetCurrentValue with UnsetValue");// Parameters should have already been validated, so we call//  into the private method to avoid validating again.ClearValueCommon(entryIndex, dp, metadata);return;}// Validate the "value" against the DP.bool isDeferredReference = false;bool newValueHasExpressionMarker = (value == ExpressionInAlternativeStore);// First try to validate the value; only after this validation fails should we// do the more expensive checks (type checks) for the less common scenariosif (!newValueHasExpressionMarker){bool isValidValue = isInternal ? dp.IsValidValueInternal(value) : dp.IsValidValue(value);// for properties of type "object", we have to always check for expression & deferredreferenceif (!isValidValue || dp.IsObjectType){// 2nd most common is expressionnewExpr = value as Expression;if (newExpr != null){// For Expressions, perform additional validation// Make sure Expression is "attachable"if (!newExpr.Attachable){throw new ArgumentException(SR.Get(SRID.SharingNonSharableExpression));}// Check dispatchers of all Sources// CALLBACKnewSources = newExpr.GetSources();ValidateSources(this, newSources, newExpr);}else{// and least common is DeferredReferenceisDeferredReference = (value is DeferredReference);if (!isDeferredReference){if (!isValidValue){// it's not a valid value & it's not an expression, so throwthrow new ArgumentException(SR.Get(SRID.InvalidPropertyValue, value, dp.Name));}}}}}// Get old valueEffectiveValueEntry oldEntry;if (operationType == OperationType.ChangeMutableDefaultValue){oldEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Default);oldEntry.Value = value;}else{oldEntry = GetValueEntry(entryIndex, dp, metadata, RequestFlags.RawEntry);}// if there's an expression in some other store, fetch it nowExpression currentExpr =(oldEntry.HasExpressionMarker)  ? _getExpressionCore(this, dp, metadata): (oldEntry.IsExpression)         ? (oldEntry.LocalValue as Expression):                                   null;// Allow expression to store value if new value is// not an Expression, if applicablebool handled = false;if ((currentExpr != null) && (newExpr == null)){// Resolve deferred references because we haven't modified// the expression code to work with DeferredReference yet.if (isDeferredReference){value = ((DeferredReference) value).GetValue(BaseValueSourceInternal.Local);}// CALLBACKhandled = currentExpr.SetValue(this, dp, value);entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex);}// Create the new effective value entryEffectiveValueEntry newEntry;if (handled)                                                                                                                                                                                        ){// If expression handled set, then doneif (entryIndex.Found){newEntry = _effectiveValues[entryIndex.Index];}else{// the expression.SetValue resulted in this value being removed from the table;// use the default value.newEntry = EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp));}coerceWithCurrentValue = false; // expression already handled the control-value}else{// allow a control-value to coerce an expression value, when the// expression didn't handle the valueif (coerceWithCurrentValue && currentExpr != null){currentExpr = null;}newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local);// detach the old expression, if applicableif (currentExpr != null){// CALLBACKDependencySource[] currentSources = currentExpr.GetSources();UpdateSourceDependentLists(this, dp, currentSources, currentExpr, false);  // Remove// CALLBACKcurrentExpr.OnDetach(this, dp);currentExpr.MarkDetached();entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex);}// attach the new expression, if applicableif (newExpr == null){// simple local value setnewEntry.HasExpressionMarker = newValueHasExpressionMarker;newEntry.Value = value;}else{Debug.Assert(!coerceWithCurrentValue, "Expression values not supported in SetCurrentValue");// First put the expression in the effectivevalueentry table for this object;// this allows the expression to update the value accordingly in OnAttachSetEffectiveValue(entryIndex, dp, dp.GlobalIndex, metadata, newExpr, BaseValueSourceInternal.Local);// Before the expression is attached it has default valueobject defaultValue = metadata.GetDefaultValue(this, dp);entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex);SetExpressionValue(entryIndex, defaultValue, newExpr);UpdateSourceDependentLists(this, dp, newSources, newExpr, true);  // AddnewExpr.MarkAttached();// CALLBACKnewExpr.OnAttach(this, dp);// the attach may have added entries in the effective value table ...// so, update the entryIndex accordingly.entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex);newEntry = EvaluateExpression(entryIndex,dp,newExpr,metadata,oldEntry,_effectiveValues[entryIndex.Index]);entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex);}}UpdateEffectiveValue(entryIndex,dp,metadata,oldEntry,ref newEntry,coerceWithDeferredReference,coerceWithCurrentValue,operationType);}
ClearValueCommon
/// <summary>///     The common code shared by all variants of ClearValue/// </summary>private void ClearValueCommon(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata){if (IsSealed){throw new InvalidOperationException(SR.Get(SRID.ClearOnReadOnlyObjectNotAllowed, this));}// Get old valueEffectiveValueEntry oldEntry = GetValueEntry(entryIndex,dp,metadata,RequestFlags.RawEntry);// Get current local value// (No need to go through read local callback, just checking// for presence of Expression)object current = oldEntry.LocalValue;// Get current expressionExpression currentExpr = (oldEntry.IsExpression) ? (current as Expression) : null;// Inform value expression of detachment, if applicableif (currentExpr != null){// CALLBACKDependencySource[] currentSources = currentExpr.GetSources();UpdateSourceDependentLists(this, dp, currentSources, currentExpr, false);  // Remove// CALLBACKcurrentExpr.OnDetach(this, dp);currentExpr.MarkDetached();entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex);}// valuesource == Local && value == UnsetValue indicates that we are clearing the local valueEffectiveValueEntry newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local);// Property is now invalidUpdateEffectiveValue(entryIndex,dp,metadata,oldEntry,ref newEntry,false /* coerceWithDeferredReference */,false /* coerceWithCurrentValue */,OperationType.Unknown);}

Mode对SetValue的影响

在wpf - 依赖属性SetValue()和SetCurrentValue()之间的区别是什么 - 堆栈溢出 — wpf - What’s the difference between Dependency Property SetValue() & SetCurrentValue() - Stack Overflow上看到和Mode还有关系,于是我又测试了一下:
在这里插入图片描述

将按钮的绑定方式改为OneWay,发现SetValue执行后,绑定为Null,绑定被销毁。

<Buttonx:Name="MyButton"Width="100"Height="50"Background="{Binding DefaultBackgroundColor,Mode=OneWay,RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"Content="MyButton" />

总结

就我的测试Demo来看,

  1. ClearValue会使得数据绑定失效。
  2. SetCurrentValue和官方文档所述一致,不会覆盖该属性的值来源,如果属性值是通过绑定、样式或触发器设置的,使用 SetCurrentValue 后,这些设置仍然有效。
  3. SetValue 设置的依赖属性为DependencyProperty.UnsetValue,会和ClearValue的表现一致。
  4. SetValue在Mode=TwoWay时,和SetCurrentValue表现一致,数据绑定不会失效。
  5. SetValue 在Mode=OneWay时。数据绑定也会失效。

参考

  1. What’s the difference between Dependency Property SetValue() & SetCurrentValue()
  2. Dependency property value precedence - WPF .NET | Microsoft Learn
  3. 源码

相关文章:

WPF|依赖属性SetCurrentValue方法不会使绑定失效, SetValue方法会使绑定失效?是真的吗?

引言 最近因为一个触发器设置的结果总是不起效果的原因&#xff0c;进一步去了解[依赖属性的优先级](Dependency property value precedence - WPF .NET | Microsoft Learn)。在学习这个的过程中发现对SetCurrentValue一直以来的谬误。 在WPF中依赖属性Dependency property的…...

Windows搭建Java开发环境(Building a Java development environment on Windows)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...

用FPGA做一个全画幅无反相机

做一个 FPGA 驱动的全画幅无反光镜数码相机是不是觉得很酷&#xff1f; 就是上图这样。 Sitina 一款开源 35 毫米全画幅 (3624 毫米) CCD 无反光镜可换镜头相机 (MILC)&#xff0c;这个项目最初的目标是打造一款数码相机&#xff0c;将 SLR [单镜头反光] 相机转换为 DSLR [数码…...

使用 Go 语言与 Redis 构建高效缓存与消息队列系统

什么是 Redis&#xff1f; Redis 是一个开源的内存数据库&#xff0c;支持多种数据结构&#xff0c;包括字符串、列表、集合、哈希和有序集合。由于 Redis 运行在内存中&#xff0c;读写速度极快&#xff0c;常被用于构建缓存系统、实时排行榜、会话存储和消息队列等高并发场景…...

springboot 整合spring ai实现 基于知识库的客服问答

rag 需求产生的背景介绍&#xff1a; 在使用大模型时&#xff0c;常遇到的问题之一是模型可能产生幻觉&#xff0c;即生成的内容缺乏准确性。此外&#xff0c;由于大模型不直接访问企业的专有数据&#xff0c;其响应可能会显得泛泛而谈&#xff0c;不够精准或具体&#xff0c;…...

云原生(四十九) | WordPress源码部署

文章目录 WordPress源码部署 一、WordPress部署步骤 二、创建项目目录 三、上传源码到WordPress 四、配置安全组 五、配置WordPress 六、访问WordPress WordPress源码部署 一、WordPress部署步骤 第一步&#xff1a;创建项目目录 第二步&#xff1a;上传源码到项目目…...

Spring Boot 集成 LiteFlow 实现业务流程编排

LiteFlow 是一款轻量级的流程编排框架,它允许开发者通过简单的配置方式,将复杂的业务流程分解为多个独立的节点,然后通过定义规则来编排节点,达到解耦业务逻辑、提高代码可维护性的目的 1. LiteFlow 的基本概念 在 LiteFlow 中,主要有以下几个概念: 节点 (Node):代表一…...

在 Android Studio 中引入android.os.SystemProperties

在 Android Studio 中引入android.os.SystemProperties 前言 网上有很多种方法&#xff0c;其中直接导入包的办法是行不通的&#xff0c;昨天自己发现问题后也踩了很多坑&#xff0c;现在把问题解决了也全面汇总了几种方法&#xff0c;确保可以百分百引入 1. layoutlib.jar包…...

代码随想录算法训练营总结

这几天一直有事情需要忙&#xff0c;所以现在来准备总结以下训练营的成果。 先说以下总体感受&#xff0c;非常值得&#xff01;&#xff01;&#xff01; 从两个月前开始跟着每天看发布的任务&#xff0c;然后每天坚持打卡&#xff0c;收获还是很大的&#xff0c;从数组开始…...

【uniapp】使用uniapp实现一个输入英文单词翻译组件

目录 1、组件代码 2、组件代码 3、调用页面 4、展示 前言&#xff1a;使用uniapp调用一个在线单词翻译功能 1、组件代码 2、组件代码 YouDaoWordTranslator <template><view class"translator"><input class"ipttext" type"te…...

6. 继承、重写、super、final

文章目录 一、重新定义需求二、继承1. 继续分析2. 概念3. 代码① 父类② 子类③ 测试结果 4. 饿狼传说之多层继承① 概念② 代码 5. 多继承 三、方法的重写1. 情境2. 代码① 吃什么② 怎么叫(Override重写) 3. 小结 四、super1. 啃老2. 啃老啃到底 五、final1. 用途及特征2. 举…...

Redis 其他类型 渐进式遍历

我们之前已经学过了Redis最常用的五个类型了&#xff0c;然而Redis还有一些在特定场景下比较好用的类型 Redis最关键的五个数据类型&#xff1a; 上面的类型是非常常用&#xff0c;很重要的类型。 除此之外的其他类型不常用&#xff0c;只是在特定的场景能够发挥用处&#…...

科研绘图系列:R语言绘制SCI文章图2

文章目录 介绍加载R包导入数据图a图b图d系统信息介绍 文章提供了绘制图a,图b和图d的数据和代码 加载R包 library(ggplot2) library(dplyr) library(readxl) library(ggpmisc)导入数据 数据可从以下链接下载(画图所需要的所有数据): 百度网盘下载链接: https://pan.baid…...

ARM知识点三和串口代码的编写流程

ARM的一些常见问题 ARM 体系结构的主要特点是什么&#xff1f; 精简指令集 (RISC)&#xff1a;ARM 采用 RISC 结构&#xff0c;指令集较小且简单&#xff0c;执行效率高。相比于复杂指令集 (CISC)&#xff0c;RISC 更强调每条指令的执行速度。低功耗设计&#xff1a;ARM 处理…...

【unity踩坑】打开vs2022没有文字联想/杂项文件

unity打开vs2022没有文字联想 修改外置编辑器安装unity开发插件vs编辑器显示杂项文件 修改外置编辑器安装unity开发插件 参考 在unity项目里选择Edit-> Preferences->External Tools然后更换编辑器 在vs工具界面添加unity游戏开发选项。 重新打开还是有问题&#xff…...

WebGoat JAVA反序列化漏洞源码分析

目录 InsecureDeserializationTask.java 代码分析 反序列化漏洞知识补充 VulnerableTaskHolder类分析 poc 编写 WebGoat 靶场地址&#xff1a;GitHub - WebGoat/WebGoat: WebGoat is a deliberately insecure application 这里就不介绍怎么搭建了&#xff0c;可以参考其他…...

大数据-161 Apache Kylin 构建Cube 按照日期、区域、产品、渠道 与 Cube 优化

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

uni-app使用v-show编译成微信小程序的问题

问题 在uni-app使用v-show语法编译成微信小程序会有一个问题 当我们设置成v-show"false" 在Hbuilder X里面确实没有显示 然后运行到 微信开发程序里面 发现显示了出来&#xff0c;说明设置的 v-show"false"没有起作用 解决办法 首先去uniapp官网查看v…...

充电宝租赁管理系统网站毕业设计SpringBootSSM框架开发

目录 1. 概述 2. 技术选择与介绍 3. 系统设计 4. 功能实现 5. 需求分析 1. 概述 充电宝租赁管理系统网站是一个既实用又具有挑战性的项目。 随着移动设备的普及和人们日常生活对电力的持续依赖&#xff0c;充电宝租赁服务已成为现代都市生活中的一项重要便利设施。它不仅为…...

喜讯!迈威通信TSN产品通过“时间敏感网络(TSN)产业链名录计划”评测,各项指标名列前茅

TSN技术&#xff0c;作为推动企业网络化与智能化转型的关键力量&#xff0c;已成为工业网络迈向下一代演进的共识方向&#xff0c;正加速重构工业网络的技术架构与产业生态。为响应这一趋势&#xff0c;工业互联网产业联盟携手中国信息通信研究院及50余家产学研用单位&#xff…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

flow_controllers

关键点&#xff1a; 流控制器类型&#xff1a; 同步&#xff08;Sync&#xff09;&#xff1a;发布操作会阻塞&#xff0c;直到数据被确认发送。异步&#xff08;Async&#xff09;&#xff1a;发布操作非阻塞&#xff0c;数据发送由后台线程处理。纯同步&#xff08;PureSync…...

作为点的对象CenterNet论文阅读

摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表&#xff0c;并对每一个位置进行分类。这种做法既浪费又低效&#xff0c;并且需要额外的后处理。在本文中&#xff0c;我们采取了不同的方法。我们将物体建模为单…...

WinUI3开发_使用mica效果

简介 Mica(云母)是Windows10/11上的一种现代化效果&#xff0c;是Windows10/11上所使用的Fluent Design(设计语言)里的一个效果&#xff0c;Windows10/11上所使用的Fluent Design皆旨在于打造一个人类、通用和真正感觉与 Windows 一样的设计。 WinUI3就是Windows10/11上的一个…...

MyBatis-Plus 常用条件构造方法

1.常用条件方法 方法 说明eq等于 ne不等于 <>gt大于 >ge大于等于 >lt小于 <le小于等于 <betweenBETWEEN 值1 AND 值2notBetweenNOT BETWEEN 值1 AND 值2likeLIKE %值%notLikeNOT LIKE %值%likeLeftLIKE %值likeRightLIKE 值%isNull字段 IS NULLisNotNull字段…...