无论是 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
    这将告诉依赖属性系统阻止这次值的更改。

参考资料

DependencyProperty.UnsetValue 的正确打开方式的更多相关文章

  1. iOS开发小技巧--相机相册的正确打开方式

    iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...

  2. Xcode 的正确打开方式——Debugging(转载)

    Xcode 的正确打开方式——Debugging   程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ...

  3. C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别

    C#语法——泛型的多种应用   本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...

  4. InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式

    InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ...

  5. Console控制台的正确打开方式

    Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ...

  6. 任务队列和异步接口的正确打开方式(.NET Core版本)

    任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ...

  7. (一)Redis for Windows正确打开方式

    目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ...

  8. List的remove()方法的三种正确打开方式

    转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题   版权声 ...

  9. C++11随机数的正确打开方式

    C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...

随机推荐

  1. 什么是ZooKeeper(一)(通俗易懂)

    以前在做别的项目时用过zk,但没有过多深入的学习,本着通俗易懂.简单方便学习成本低的方式,建议大家耐心看完,如果文章中有不清楚的地方,可发私信进步探讨! 学习zk共分为二部分,第一部分主要以理论为主. ...

  2. Swoft 快速上手小贴士

    IDE一定要装注解插件PHP Annotations Request和Response里的with...开头的方法会clone $this, 而不是修改本实体, 所以设置Cookie之类的时候要$re ...

  3. JSP 页面重定向

    当需要将文档移动到一个新的位置时,就需要使用JSP重定向了. 最简单的重定向方式就是使用response对象的sendRedirect()方法.这个方法的签名如下: public void respo ...

  4. 小米2S刷Android4.4且双系统共存

    视频教程 http://v.youku.com/v_show/id_XNjQxNzQ5NDAw.html 必备知识 进入Recovery模式:同时按住电源键+音量上键,等到屏幕亮起时,放开电源键. 特 ...

  5. 24.Java中atomic包中的原子操作类总结

    1. 原子操作类介绍 在并发编程中很容易出现并发安全的问题,有一个很简单的例子就是多线程更新变量i=1,比如多个线程执行i++操作,就有可能获取不到正确的值,而这个问题,最常用的方法是通过Synchr ...

  6. torch 深度学习 (2)

    torch 深度学习 (2) torch ConvNet 前面我们完成了数据的下载和预处理,接下来就该搭建网络模型了,CNN网络的东西可以参考博主 zouxy09的系列文章Deep Learning ...

  7. Spark安装和简单示例

    spark的安装 先到官网下载安装包 注意第二项要选择和自己hadoop版本相匹配的spark版本,然后在第4项点击下载.若无图形界面,可用windows系统下载完成后传送到centos中. 本例中安 ...

  8. CentOS 6.5 升级内核到 3.10.28

    本文适用于CentOS 6.4, CentOS 6.5,亲测可行,估计也适用于其他Linux发行版. 1. 准备工作 1.1 下载源码包 Linux内核版本有两种:稳定版和开发版 ,Linux内核版本 ...

  9. MySQL简单的操作,增删改查

    B/S架构模式与C/S架构模式的区别 B/S=WEB/SERVER C/S=CLIENT/SERVIR B/S:用户通过web浏览器打开域名就能访问服务器server的方式就叫做B/S用户不需要安装任 ...

  10. iOS 可能用到的三方框架

    1.MWPhotoBrowser 第三方图片浏览器 https://github.com/mwaterfall/MWPhotoBrowser 2.SlackTextViewController  强大 ...