DependencyProperty.UnsetValue 的正确打开方式
无论是 WPF,还是 UWP,只要你用了绑定或者标记扩展,一定会碰到一个神奇的值——DependencyProperty.UnsetValue。UnsetValue 是什么意思?为什么会出现这个值呢?如果要让 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:
- 在用于绑定的转换器 IValueConverterIMultiValueConverter的代码里面;
- 在 XAML 标记扩展 MarkupExtension里面。
而以上两处代码,只有在发生以下三种情况时才会遇到 DependencyProperty.UnsetValue:
- 绑定出现了错误,也就是说绑定从最开始的源值到目标值的若干次转换过程中任何阶段发生了错误以至于无法成功转换到目标值。 
 虽然我们写的是一个{Binding XXX},但XXX可能由另外的绑定来提供(例如逻辑父控件的 DataContext)。一次次绑定的源值是上一个绑定的目标值,于是这样的关系组合成一个绑定提供值的链条。链条中只要有一处不能提供合理的值,就会在绑定中得到UnsetValue。
- 绑定或者标记扩展写在了 ControlTemplate或者DataTemplate里面,但此时并没有指定数据源。
 在模板应用到实际的控件之前,模板本身也会执行一次Binding和MarkupExtension的逻辑。于是如果绑定需要依赖于实际的控件,那么实际上Binding和MarkupExtension会至少执行两次,其中第一次便是模板中的那一次。此时获取依赖属性的值时拿到的便是DependencyProperty.UnsetValue。
- 使用依赖项属性的 ReadLocalValue来获取值,而不是GetValue;但此时并没有为依赖对象设置值。
 如果没有设置值,那么GetValue会返回更低优先级的值,一般情况下是依赖项属性在注册时的默认值;但ReadLocalValue就是在获取显式设置的那个值,如果没设,就只能是DependencyProperty.UnsetValue了。
我们应该如何正确使用 DependencyProperty.UnsetValue?
微软官方对于 DependencyProperty.UnsetValue 的介绍,专门的文档中只有一个说法,就是用来表示“不合理”的值,却并没有说明什么情况下为合理,什么情况下为不合理。但好在微软将一些推荐写法散落在了多个不同的文章中。这里整理在一起,以便为大家对 DependencyProperty.UnsetValue 的正确使用提供指导。
- 在注册依赖项属性的时候,不要使用 DependencyProperty.UnsetValue作为默认值。
 这个值本意其实并不是在说“未设置”,而是代表“不合理”。默认值必须是“合理地”才行。微软官方文档 Custom dependency properties 对此的解释是,如果默认值设置为UnsetValue,则会在大家使用其值的时候产生混淆,并不能区分到底是依赖属性(的绑定系统)提供值的时候出错了还是因为只是默认没设置。
- 在写绑定的转换器的时候,如果转换有错误,不应该抛出异常,而是应该返回一个 DependencyProperty.UnsetValue,以便阻止绑定中继续传递值。
 微软在 Data binding in depth 中写出了这个要求,而在 How to: Convert Bound Data 中给出了示例代码。
- 如果需要在 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
DependencyProperty.UnsetValue 的正确打开方式的更多相关文章
- iOS开发小技巧--相机相册的正确打开方式
		iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ... 
- Xcode 的正确打开方式——Debugging(转载)
		Xcode 的正确打开方式——Debugging 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ... 
- C#语法——泛型的多种应用  C#语法——await与async的正确打开方式  C#线程安全使用(五)  C#语法——元组类型  好好耕耘 redis和memcached的区别
		C#语法——泛型的多种应用 本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ... 
- InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式
		InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ... 
- Console控制台的正确打开方式
		Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ... 
- 任务队列和异步接口的正确打开方式(.NET Core版本)
		任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ... 
- (一)Redis for Windows正确打开方式
		目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ... 
- List的remove()方法的三种正确打开方式
		转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题 版权声 ... 
- C++11随机数的正确打开方式
		C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ... 
随机推荐
- .net 获取浏览器Cookie(包括HttpOnly)
			网上好不容易找到的,分享+收藏 一.接口文件 using System; using System.ComponentModel; using System.Net; using System.Run ... 
- poi读取excel转对象,格式转换帮助类
			//格式转换//value:原数据,parmtype:方法参数类型,cellType 单元格类型public static Object formatd(String value, String pa ... 
- Web 安全测试,盗版小坦克
			Web安全测试之XSS XSS 全称(Cross Site Scripting) 跨站脚本攻击, 是Web程序中最常见的漏洞.指攻击者在网页中嵌入客户端脚本(例如JavaScript), 当用户浏览此 ... 
- WPF:理解TileBrush(ImageBrush,DrawingBrush和VisualBrush)
			ImageBrush:利用图像绘制区域 ImageBrush 是一种将自身内容定义为图像的 TileBrush,图像通过它的 ImageSource 属性指定. 您可以控制图像的拉伸.对齐和平铺方式, ... 
- Mysql5.7基于日志转为基于事务主从复制
			将基于日志的复制变更为基于事务的复制 mysql版本要高于5.7.6 gtid_mode要设为off 处理步骤 详细步骤 1.查看主从mysql版本是否高于5.7.6 show variables l ... 
- HDU 4417 BIT or ST
			Super Mario Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ... 
- spring 核心接口之 Ordered
			Spring中提供了一个Ordered接口.从单词意思就知道Ordered接口的作用就是用来排序的.Spring框架是一个大量使用策略设计模式的框架,这意味着有很多相同接口的实现类,那么必定会有优先级 ... 
- WordCount-个人项目2
			我的这个WC程序实现了对txt文件中的数据的计数,算出程序中有多少单词.字符数.行数还有空格数.以及长度. 项目源代码参考:http://www.cnblogs.com/sunbuqiao/p/531 ... 
- Unix系统中system函数的返回值
			网上关于system函数的返回值说明很多很详细但却不直观,这里搬出apue 3rd Editon中实现system函数的代码来说明其返回值. #include <sys/wait.h> # ... 
- 2018.11.13 N4010A 通信设置
			设置电脑之IP地址及Subnet mask. IP address: 192.168.1.2 Subnet mask: 255.255.255.0, 其它选项为默认. 然后点击OK ... 
