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. 源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/444044.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从0到1:小区业主决策投票小程序开发笔记

可研 小区业主决策投票小程序&#xff1a; 便于业主参与社区事务的决策&#xff0c;通过网络投票的形式&#xff0c;大大节省了业委会和业主时间&#xff0c;也提高了投票率。其主要功能&#xff1a;通过身份证、业主证或其他方式确认用户身份&#xff1b;小区管理人员或业委会…

Java 17流程控制语句3w字解读

本笔记来自尚硅谷教育-康师傅&#xff0c;学习教程&#xff1a;https://www.bilibili.com/video/BV1PY411e7J6/?spm_id_from333.337.search-card.all.click 本章专题与脉络 第1阶段&#xff1a;Java基本语法-第03章 流程控制语句是用来控制程序中各语句执行顺序的语句&#xf…

5个免费ppt模板网站推荐!轻松搞定职场ppt制作!

每次过完小长假&#xff0c;可以明显地感觉到&#xff0c;2024这一年很快又要结束了&#xff0c;不知此刻的你有何感想呢&#xff1f;是满载而归&#xff0c;还是准备着手制作年终总结ppt或年度汇报ppt呢&#xff1f; 每当说到制作ppt&#xff0c;很多人的第一反应&#xff0c…

threejs-基础材质设置

一、介绍 主要内容&#xff1a;基础材质(贴图、高光、透明、环境、光照、环境遮蔽贴图) 主要属性&#xff1a; side: three.DoubleSide, //设置双面 color: 0xffffff, //颜色 map: texture, //纹理 transparent: true, // 透明度 aoMap: aoTexture, //ao贴图 aoMapIntensity: 1…

YOLO11改进|注意力机制篇|引入局部注意力HaloAttention

目录 一、【HaloAttention】注意力机制1.1【HaloAttention】注意力介绍1.2【HaloAttention】核心代码 二、添加【HaloAttention】注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【HaloAttention】注意力机制 1.1【HaloAttent…

使用FastAPI做人工智能后端服务器时,接口内的操作不是异步操作的解决方案

在做AI模型推理的接口时&#xff0c;这时候接口是非异步的&#xff0c;但是uvicorn运行FastAPI时就会出现阻塞所有请求。 这时候需要解决这个问题&#xff1a; api.py&#xff1a; import asyncio from fastapi import FastAPI from fastapi.responses import StreamingResp…

嵌入式开发:STM32 硬件 CRC 使用

测试平台&#xff1a;STM32G474系列 STM32硬件的CRC不占用MCU的资源&#xff0c;计算速度快。由于硬件CRC需要配置一些选项&#xff0c;配置不对就会导致计算结果错误&#xff0c;导致使用上没有软件计算CRC方便。但硬件CRC更快的速度在一些有时间资源要求的场合还是非…

从 Reno TCP 到 Scalable TCP,HighSpeed TCP

前文 Scalable TCP 如何优化长肥管道 介绍了 Scalable TCP&#xff0c;但联系另一个类似的算法 HighSpeed TCP(简称 HSTCP)&#xff0c;就会看到一个类似从 Reno TCP 经 BIC 到 CUBIC 的路线&#xff0c;但采用了不同的策略。 Reno TCP 经 BIC 到 CUBIC 路线的核心在于 “在长…

4反馈、LC、石英、RC振荡器

1什么是振荡器&#xff1f; 我们看看振荡器在无线通信中扮演什么角色&#xff1f; 1&#xff09;无线通信的波是指电磁波‌。 2‌&#xff09;电磁波的频率高于100KHz才能在空气中传播。‌ 3&#xff09;空气中的高频电磁波的相位和振幅可以排列组合包含信息。 4&#xff09;无…

DBMS-3.4 SQL(4)——存储过程和函数触发器

本文章的素材与知识来自李国良老师和王珊老师。 存储过程和函数 一.存储过程 1.语法 2.示例 &#xff08;1&#xff09; 使用DELIMITER更换终止符后用于编写存储过程语句后&#xff0c;在下次执行SQL语句时记得再使用DELIMITER将终止符再换回分号。 使用DELIMITER更换终止符…

Ubuntu 22.04.4 LTS更换下载源

方法1&#xff1a;使用图形界面更换下载源 1. 打开软件和更新应用 2. 在Ubuntu 软件标签中&#xff0c;点击“下载自”旁边的下拉菜单&#xff0c;选择“其他” 3. 点击“选择最佳服务器”来自动选择最快的服务器 4. 选择服务器 5. 确定并关闭窗口&#xff0c;系统会提示您重新…

ElasticSearch备考 -- Multi match

一、题目 索引task有3个字段a、b、c&#xff0c;写一个查询去匹配这三个字段为mom&#xff0c;其中b的字段评分比a、c字段大一倍&#xff0c;将他们的分数相加作为最后的总分数 二、思考 通过题目要求对多个字段进行匹配查询&#xff0c;可以考虑multi match、bool query操作。…

【C++第十八章】Map和Set

Map和Set map和set的介绍 容器分为两种&#xff0c;序列式容器和关联式容器&#xff0c;序列式容器因为底层是线性序列的数据结构&#xff0c;存储的是元素本身&#xff0c;而关联式容器中不单是为了存储数据&#xff0c;还要进行查找&#xff0c;所以存储的是键值对&#xff…

网络编程(17)——asio多线程模型IOThreadPool

十七、day17 之前我们介绍了IOServicePool的方式&#xff0c;一个IOServicePool开启n个线程和n个iocontext&#xff0c;每个线程内独立运行iocontext, 各个iocontext监听各自绑定的socket是否就绪&#xff0c;如果就绪就在各自线程里触发回调函数。为避免线程安全问题&#xf…

腾讯云SDK点播播放数据

点播播放质量监控提供点播播放全链路的数据统计、质量监控及可视化分析服务。支持实时数据上报、数据聚合、多维筛选和精细化定向分析&#xff0c;可帮助企业实时掌控大盘运营状况、了解用户习惯和行为特征&#xff0c;有效指导运营决策、驱动业务增长。 注意事项 点播播放质…

Python 工具库每日推荐 【Pandas】

文章目录 引言Python数据处理库的重要性今日推荐:Pandas工具库主要功能:使用场景:安装与配置快速上手示例代码代码解释实际应用案例案例:销售数据分析案例分析高级特性数据合并和连接时间序列处理数据透视表扩展阅读与资源优缺点分析优点:缺点:总结【 已更新完 TypeScrip…

基于 CSS Grid 的简易拖拉拽 Vue3 组件,从代码到NPM发布(1)- 拖拉拽交互

基于特定的应用场景&#xff0c;需要在页面中以网格的方式&#xff0c;实现目标组件在网格中可以进行拖拉拽、修改大小等交互。本章开始分享如何一步步从代码设计&#xff0c;最后到如何在 NPM 上发布。 请大家动动小手&#xff0c;给我一个免费的 Star 吧~ 大家如果发现了 Bug…

探索未来:mosquitto-python,AI领域的新宠

文章目录 探索未来&#xff1a;mosquitto-python&#xff0c;AI领域的新宠背景&#xff1a;为何选择mosquitto-python&#xff1f;库简介&#xff1a;mosquitto-python是什么&#xff1f;安装指南&#xff1a;如何安装mosquitto-python&#xff1f;函数用法&#xff1a;5个简单…

代码随想录算法训练营第四十六天 | 647. 回文子串,516.最长回文子序列

四十六天打卡&#xff0c;今天用动态规划解决回文问题&#xff0c;回文问题需要用二维dp解决 647.回文子串 题目链接 解题思路 没做出来&#xff0c;布尔类型的dp[i][j]&#xff1a;表示区间范围[i,j] &#xff08;注意是左闭右闭&#xff09;的子串是否是回文子串&#xff0…

深入理解Transformer的笔记记录(精简版本)---- Transformer

自注意力机制开启大规模预训练时代 1 从机器翻译模型举例 1.1把编码器和解码器联合起来看待的话,则整个流程就是(如下图从左至右所示): 1.首先,从编码器输入的句子会先经过一个自注意力层(即self-attention),它会帮助编码器在对每个单词编码时关注输入句子中的的其他单…