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

DependencyProperty.UnsetValue 的正确打开方式

无论是 WPF,还是 UWP,只要你用了绑定或者标记扩展,一定会碰到一个神奇的值——DependencyProperty.UnsetValueUnsetValue 是什么意思?为什么会出现这个值呢?如果要让 UnsetValue 为我们所用,正确的用法又是什么呢?


DependencyProperty.UnsetValue 是什么?

要知道这是什么,一定要看源码:

/// <summary> Standard unset value </summary>
public static readonly object UnsetValue = new NamedObject("DependencyProperty.UnsetValue");

这是一个 NamedObject,而 NamedObject 又是什么呢?

internal class NamedObject
{
    public NamedObject(string name)
    {
        if (String.IsNullOrEmpty(name)) throw new ArgumentNullException(name);
        _name = name;
    }
    public override string ToString()
    {
        if (_name[0] != '{') _name = String.Format(CultureInfo.InvariantCulture, "{{{0}}}", _name);
        return _name;
    }
    string _name;
}

好吧,其实这个类根本就没有什么用途,微软只是随便找了一个类,以便你在 Visual Studio 调试器或者你自己用代码输出值的时候能够显示一个预设好的字符串。真的只是起调试作用的啊!

DependencyProperty.UnsetValue 的定义中,只是为了让大家调试的时候显示 DependencyProperty.UnsetValue 而已。值本身不代表任何意义,只是为了说明遇到了一个“未设置”的值。

但是有人会问:null 在调试的时候也会显示 null 啊,为啥不用 null,要特别准备一个值呢?

这是因为在绑定中,null 可能是一个合理的值,可能会被故意用在绑定中来达到某种目的。于是微软必须用一个大家平常开发中一定不会用到的值来表示“不合理”,于是祭出了 DependencyProperty.UnsetValue

什么情况下会出现 DependencyProperty.UnsetValue?

正常情况下,只有以下两处代码会遇到 DependencyProperty.UnsetValue

  1. 在用于绑定的转换器 IValueConverter IMultiValueConverter 的代码里面;
  2. 在 XAML 标记扩展 MarkupExtension 里面。

而以上两处代码,只有在发生以下三种情况时才会遇到 DependencyProperty.UnsetValue

  1. 绑定出现了错误,也就是说绑定从最开始的源值到目标值的若干次转换过程中任何阶段发生了错误以至于无法成功转换到目标值。
    虽然我们写的是一个 {Binding XXX},但 XXX 可能由另外的绑定来提供(例如逻辑父控件的 DataContext)。一次次绑定的源值是上一个绑定的目标值,于是这样的关系组合成一个绑定提供值的链条。链条中只要有一处不能提供合理的值,就会在绑定中得到 UnsetValue
  2. 绑定或者标记扩展写在了 ControlTemplate 或者 DataTemplate 里面,但此时并没有指定数据源。
    在模板应用到实际的控件之前,模板本身也会执行一次 BindingMarkupExtension 的逻辑。于是如果绑定需要依赖于实际的控件,那么实际上 BindingMarkupExtension 会至少执行两次,其中第一次便是模板中的那一次。此时获取依赖属性的值时拿到的便是 DependencyProperty.UnsetValue
  3. 使用依赖项属性的 ReadLocalValue 来获取值,而不是 GetValue;但此时并没有为依赖对象设置值。
    如果没有设置值,那么 GetValue 会返回更低优先级的值,一般情况下是依赖项属性在注册时的默认值;但 ReadLocalValue 就是在获取显式设置的那个值,如果没设,就只能是 DependencyProperty.UnsetValue 了。

我们应该如何正确使用 DependencyProperty.UnsetValue?

微软官方对于 DependencyProperty.UnsetValue 的介绍,专门的文档中只有一个说法,就是用来表示“不合理”的值,却并没有说明什么情况下为合理,什么情况下为不合理。但好在微软将一些推荐写法散落在了多个不同的文章中。这里整理在一起,以便为大家对 DependencyProperty.UnsetValue 的正确使用提供指导。

  1. 在注册依赖项属性的时候,不要使用 DependencyProperty.UnsetValue 作为默认值。
    这个值本意其实并不是在说“未设置”,而是代表“不合理”。默认值必须是“合理地”才行。微软官方文档 Custom dependency properties 对此的解释是,如果默认值设置为 UnsetValue,则会在大家使用其值的时候产生混淆,并不能区分到底是依赖属性(的绑定系统)提供值的时候出错了还是因为只是默认没设置。
  2. 在写绑定的转换器的时候,如果转换有错误,不应该抛出异常,而是应该返回一个 DependencyProperty.UnsetValue,以便阻止绑定中继续传递值。
    微软在 Data binding in depth 中写出了这个要求,而在 How to: Convert Bound Data 中给出了示例代码。
  3. 如果需要在 CoerceValueCallback 回调中验证值的合理性,当值不合理的时候,返回 DependencyProperty.UnsetValue
    这将告诉依赖属性系统阻止这次值的更改。

参考资料

  • Data binding in depth - UWP app developer - Microsoft Docs
  • How to: Convert Bound Data - Microsoft Docs
  • Custom dependency properties - UWP app developer - Microsoft Docs
  • Dependency Property Callbacks and Validation - Microsoft Docs
  • c# - Why do I get a DependencyProperty.UnsetValue when converting a value in a MultiBinding? - Stack Overflow
  • DependencyProperty.UnsetValue Field (System.Windows)
  • UnsetValue

相关文章:

  • 如何组织一个同时面向 UWP/WPF/.Net Core 控制台的 C# 项目解决方案
  • 出让执行权:Task.Yield, Dispathcer.Yield
  • 如何防止后台线程抛出的异常让程序崩溃退出
  • CaptureMouse/CaptureStylus 可能会失败
  • 使用 ExceptionDispatchInfo 捕捉并重新抛出异常
  • 使用 Task.Wait()?立刻死锁(deadlock)
  • 如何实现一个可以用 await 异步等待的 Awaiter
  • WPF 同一窗口内的多线程 UI(VisualTarget)
  • WPF 和 UWP 中,不用设置 From 或 To,Storyboard 即拥有更灵活的动画控制
  • 从 “x is null 和 x == null” 的区别看 C# 7 模式匹配中常量和 null 的匹配
  • 使用不安全代码将 Bitmap 位图转为 WPF 的 ImageSource 以获得高性能和持续小的内存占用
  • WPF 跨应用程序域的 UI(Cross AppDomain UI)
  • 将 UWP 的有效像素(Effective Pixels)引入 WPF
  • 用动画的方式画出任意的路径(直线、曲线、折现)
  • 使 WPF 支持触摸板的横向滚动
  • [nginx文档翻译系列] 控制nginx
  • Android Studio:GIT提交项目到远程仓库
  • Centos6.8 使用rpm安装mysql5.7
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • ES6--对象的扩展
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • leetcode46 Permutation 排列组合
  • React+TypeScript入门
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • springMvc学习笔记(2)
  • windows下如何用phpstorm同步测试服务器
  • 从伪并行的 Python 多线程说起
  • 第十八天-企业应用架构模式-基本模式
  • 对象引论
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 深入浅出Node.js
  • 数据仓库的几种建模方法
  • 思考 CSS 架构
  • 线性表及其算法(java实现)
  • 最简单的无缝轮播
  • Java性能优化之JVM GC(垃圾回收机制)
  • MPAndroidChart 教程:Y轴 YAxis
  • MyCAT水平分库
  • puppet连载22:define用法
  • 数据可视化之下发图实践
  • ![CDATA[ ]] 是什么东东
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #HarmonyOS:Web组件的使用
  • (初研) Sentence-embedding fine-tune notebook
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (四)鸿鹄云架构一服务注册中心
  • (转)Linux下编译安装log4cxx
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .cn根服务器被攻击之后
  • .form文件_一篇文章学会文件上传
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .NET MVC第三章、三种传值方式
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET Standard 的管理策略