本文来和大家聊聊在 WPF 里面在给 ResourceDictionary 设置 Source 属性时,在 WPF 框架里面做了哪些逻辑
默认添加 Source 时都是指定 WPF 自身的 XAML 资源字典,用途就是指定 XAML 字典作为此控件的资源字典
而默认的 XAML 资源字典使用 Page 形式进行构建,构建之后作为二进制的 Baml 文件被打入程序集中作为程序集资源,在 dotnet 里面有专门的程序集 System.IO.Packaging 来解析程序集资源
而给 Source 属性赋值的时候,给的是一个 Uri 类型的变量,那么 资源字典 ResourceDictionary 是如何通过 Uri 拿到对应的内容的?其实在 WPF 的 ResourceDictionary 的 Source 属性赋值里面有很长的一段逻辑,如下面代码,请大家快速跳过,这些代码只是用来告诉大家,在 WPF 里面使用了很多代码来处理这部分逻辑
大概整理一下的在 Source 的 set 方法里面的逻辑大概如下
可以看到上面代码的逻辑步骤其实很少,核心的逻辑就是 解析 Uri 获取资源 这部分
在开始获取资源之前,需要先将 Uri 转换为绝对路径,也就是说在 XAML 中写的 Uri 将会被补全
这里拿到 uri 之后,通过调用 WpfWebRequestHelper 的方法拿到资源的 Stream 对象
看到了 WebRequest 请不要激动,这不代表一定会从网络上读取哦
因为这个 WebRequest 是使用 WpfWebRequestHelper 的 CreateRequest 拿到的 WebRequest 不一定是一个走网络的 WebRequest 哦,在 WpfWebRequestHelper 的 CreateRequest 方法里面,会根据 Uri 进行判断,假定是获取到一个在应用本地资源的路径,那么将使用 PackWebRequestFactory.CreateWebRequest 返回一个基于 System.IO.Packaging 的 PackWebRequest 对象。否则就是真的走网络了,因此给资源字典设置一个网络上的 Url 也是可以的
在 PackWebRequest 里面,其实就是一个继承了 WebRequest 的类,这个类的命名空间是 System.IO.Packaging 但是放在 PresentationCore 里面,是逻辑上属于 System.IO.Packaging 程序集,但实际上在 PresentationCore 程序集
在 PackWebRequest 通过重写 WebRequest 的方法,实现了实际上没有走网络,而是返回了 PackWebResponse 对象,在 PackWebResponse 里面就是读取程序集的资源作为 Stream 返回
因此调用 WpfWebRequestHelper 的 CreateRequest 方法创建的 WebRequest 在传入的是 uri 是一个本地的资源字典的时候,就是读取本地程序集资源返回 Stream 对象
读取到 Stream 之后需要进行解析,如下面代码
此时拿到了 loadedResourceDictionary 也就是 loadedRD 变量,下一步就是取出里面的值
此时就完成了资源字典的从 Uri 加载了
在资源字典里面,包含了两层内容,第一层的内容就是在这个资源字典里面定义的资源,这些资源放在了 private Hashtable _baseDictionary
里面。第二层内容就是 private ObservableCollection<ResourceDictionary> _mergedDictionaries
被这个资源字典合并的其他资源字典里面
因此在 WPF 中寻找资源是先从自己的 _baseDictionary
尝试获取资源,如获取不到在从 _mergedDictionaries
里面获取,如下面代码
从上面代码可以看到,获取的时候是优先从 _baseDictionary
获取的。获取不到在从 MergedDictionaries 里面获取,最后添加的资源字典最先寻找。也就是说存在 Key 重复的资源的时候,会先从资源字典本身寻找,如果找不到就从合并的其他字典的最后一个资源字典开始寻找
这就是 WPF 资源字典设置的逻辑
当前整个 WPF 源代码都是开源的,请看 https://github.com/dotnet/wpf/
更多资源字典相关请看
原文链接: http://blog.lindexi.com/post/WPF-%E6%BA%90%E4%BB%A3%E7%A0%81-%E8%B5%84%E6%BA%90%E5%AD%97%E5%85%B8-ResourceDictionary-%E8%AE%BE%E7%BD%AE-Source-%E5%B1%9E%E6%80%A7%E7%9A%84%E9%80%BB%E8%BE%91
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。