大家都知道,在 WPF 里面,可以让资源字典合并其他资源字典,从而定义出资源字典引用树。然而在资源字典引用树里面,如果没有理清关系,将可以作出一个超级复杂的引用关系网。如果在性能优化中,将网断开部分,可能就会出现找不到资源的情况。本文将告诉大家 WPF 的资源字典树在引用和寻找关系上的坑

在开始之前先来演示一下正确的使用方法,也是绝大部分的项目和开发者最常用的方法。 也就是说,如果正常的做,是不会踩到坑的,只有在进行不良设计时才会踩坑

在 App.xaml 里面是作为资源字典的引用的 Root 最顶层,基础玩法都是在 App.xaml 引用其他资源字典,引用顺序基本上基础库,控件库,共用资源,共用样式,业务资源。如果顺序反了,很快就可以在运行应用时找不到资源炸了

例如有 DictionaryA.xaml 和 DictionaryB.xaml 和 DictionaryC.xaml 三个资源字典,在 DictionaryB 里面是共用样式,在 DictionaryC 里面是共用资源。在 DictionaryB 里面的样式引用了 DictionaryC 的资源。此时如果让 DictionaryB 通过 MergedDictionaries 的方式引用 DictionaryC 字典,将存在一个性能问题,那就是在创建资源的时候,如果在 App.xaml 里面也引用了 DictionaryC 那么将让 DictionaryC 被创建两次。一次是在 App.xaml 里面的,一次是在被 DictionaryB 的 MergedDictionaries 创建的,换句话说将会让 DictionaryC 里面的对象重复两次定义,占用资源也添加了启动时间

常用的优化方式就是只在 App.xaml 引用 DictionaryC 即可,不在 DictionaryB 里面加上引用。如果真的需要有设计时帮助,如让 VisualStudio 开启智能(zhàng)提示,那可以使用 d: 设计时资源形式。如此即可让 DictionaryC 只在 App.xaml 里面初始化一份,减少 DictionaryC 的重复创建和减少内存占用,提升了性能

例如在 DictionaryC 里面作为共用资源,定义了画刷资源,如下

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="SolidColorBrush1InC" Color="#565656"/>
</ResourceDictionary>

在 DictionaryB 里面定义了样式,样式需要用到 SolidColorBrush1InC 资源,代码如下

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<d:ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryC.xaml"/>
</d:ResourceDictionary.MergedDictionaries> <Style x:Key="ButtonStyleInB" TargetType="Button">
<Setter Property="Background" Value="{StaticResource SolidColorBrush1InC}" />
</Style>
</ResourceDictionary>

在 DictionaryB 里面不会再次合入 DictionaryC 字典,而是统一在 App.xaml 里面将两个资源字典合入。以上代码里面,包含了为了让 VisualStudio 能在设计时帮你找到资源加上的 d: 合并逻辑,这个逻辑不会在运行时有任何作用

在 App.xaml 里面的合入代码如下

<Application x:Class="GeacejalcurnawLarjearemwhear.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GeacejalcurnawLarjearemwhear"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryC.xaml"/>
<ResourceDictionary Source="DictionaryB.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

合入资源字典是有顺序要求的,如果是先合入 DictionaryB 再合入 DictionaryC 将会在 DictionaryB 里面需要引用资源时找不到资源

System.Windows.Markup.XamlParseException:““{DependencyProperty.UnsetValue}”不是属性“Background”的有效值。”

以上的测试代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 6f0ed747acf089095a8503bc8ff967c97efe9de5

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 GeacejalcurnawLarjearemwhear 文件夹

当然了,对于大部分的开发模型来说,都不会在次级的资源字典里面存放具体的资源或样式等的定义。例如没有在 App.xaml 引用 DictionaryB 资源字典,而是将 DictionaryB 放入到 DictionaryA 里面引用,关系如下

这个引用关系是没有问题的,依然可以在资源字典 DictionaryB 里面找到 DictionaryC 的资源。更新之后的代码放在 githubgitee 欢迎访问

那如果继续让 DictionaryC 的实际定义放在更底层呢?例如引入 DictionaryD.xaml 定义的资源呢,引用的关系如下

在 DictionaryC.xaml 的代码变更如下

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryD.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="SolidColorBrush1InC" Color="#565656"/>
</ResourceDictionary>

在 DictionaryD.xaml 定义了资源

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="SolidColorBrush1InD" Color="#565656"/>
</ResourceDictionary>

修改 DictionaryB.xaml 的代码,让 DictionaryB.xaml 的 ButtonStyleInB 的背景采用 SolidColorBrush1InD 资源

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonStyleInB" TargetType="Button">
<Setter Property="Background" Value="{StaticResource SolidColorBrush1InD}" />
</Style>
</ResourceDictionary>

运行程序,可以看到,在进行多级引用时,是可以成功在 DictionaryB.xaml 找到 DictionaryD.xaml 资源。也就是说在不同的两个资源字典树,一个在 DictionaryA 一个在 DictionaryC 字典树上的资源,是可以相互寻找到的

更新之后的代码放在 githubgitee 欢迎访问

同理,再次提升层级进行测试,结果依然是能找到资源的。再定义 DictionaryE.xaml 和 DictionaryF.xaml 资源字典,让 DictionaryE.xaml 去引用 DictionaryF.xaml 的资源,其引用关系如下

更新之后的代码放在 githubgitee 欢迎访问

通过以上的测试可以了解到,在去掉 App.xaml 这个 Root 顶层资源之后的多个不同的资源字典树,多个资源字典树的资源是可以被跨资源字典树进行引用的,和存放的层级无关

这也是非常符合预期的,通过这个功能,即可将需要复用的资源分开,减少重复的定义,提升界面资源的模块化

但是又有一项带坑的设计,那就是在除了 App.xaml 这个 Root 顶层资源之后的资源字典树,在资源字典树内是不能跨节点引用。例如以下的关系,将会找不到资源

如上图,在 DictionaryA.xaml 资源字典里面引用了 DictionaryC.xaml 和 DictionaryB.xaml 两个资源字典,代码如下

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryC.xaml"/>
<ResourceDictionary Source="DictionaryB.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

依然在 DictionaryC.xaml 里面定义资源

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="SolidColorBrush1InC" Color="#565656"/>
</ResourceDictionary>

在 DictionaryB.xaml 进行引用 SolidColorBrush1InC 资源,代码和上文的一样

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonStyleInB" TargetType="Button">
<Setter Property="Background" Value="{StaticResource SolidColorBrush1InC}" />
</Style>
</ResourceDictionary>

然而运行将会提示找不到 SolidColorBrush1InC 资源

大家可以尝试一下这个更新之后的代码,更新之后的代码放在 githubgitee 欢迎访问

可以通过如下方式获取更新后代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 66820e750fb1b5a104b3b4582dd31ac7393439bb

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 GeacejalcurnawLarjearemwhear 文件夹

也就是说在一个顶层的资源字典,非 App.xaml 哦,这个可不是资源字典,这个字典里面如果同时包含了共用资源和具体的样式,那如果在具体的样式里面用到任何共用资源,将会找不到共用的资源。这个就是本文要来告诉大家的 WPF 的已知问题

对于一些基础库来说,由于特殊的逻辑,不想分开两个资源字典,尽管分开两个资源字典更方便顶层业务层的定制需求,但是由于有特殊的需求而不想分开的,可以将 StaticResourceExtension 换成 DynamicResourceExtension 引用。利用 DynamicResourceExtension 会自动更新的机制,在 App.xaml 初始化资源字典的时候,实际访问将会重新去 App.xaml 寻找,从而找到资源

更改 DictionaryB.xaml 的代码如下

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonStyleInB" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource SolidColorBrush1InC}" />
</Style>
</ResourceDictionary>

虽然换用 DynamicResourceExtension 在性能上是比不过 StaticResourceExtension 的,但好在如果数量不超过几万项的话,这部分降低的性能很少

这个问题我也报告给了 WPF 官方,请看 The StaticResourceExtension will not find the resources of the ResourceDictionary of the sibling node · Issue #6627 · dotnet/wpf

WPF 已知问题 资源字典树引用与资源寻找的坑的更多相关文章

  1. wpf资源嵌套,一个资源引用另外一个资源,被引用的资源应该声明在前面

    在wpf的XAML的Window.Resources中,一个资源引用另外一个资源,出现如下错误: “错误 1 “{DependencyProperty.UnsetValue}”不是 Setter 上“ ...

  2. (转载)资源字典(Pro WPF 学习)

    原地址:http://www.cnblogs.com/yxhq/archive/2012/07/09/2582508.html 1.创建资源字典 下面是一个资源字典(AppBrushes.xaml), ...

  3. WPF之资源字典zz

    最近在看wpf相关东西,虽然有过两年的wpf方面的开发经验,但是当时开发的时候,许多东西一知半解,至今都是模模糊糊,框架基本是别人搭建,自己也就照着模板写写,现在许多东西慢慢的理解了,回顾以前的若干记 ...

  4. WPF学习笔记-使用自定义资源字典(style)文件

    1.添加资源字典文件style.xmal 2.在资源字典中添加自定义style等 <ResourceDictionary xmlns="http://schemas.microsoft ...

  5. WPF使用资源字典组织资源

    转载:http://blog.163.com/wangzhenguo2005@126/blog/static/371405262010111413321728/     首先在解决方案资源管理器中添加 ...

  6. WPF资源字典的使用【转】

    资源字典出现的初衷就在于可以实现多个项目之间的共享资源,资源字典只是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情. 1.  创建资源字典 创建资源字典的过程比较简单,只 ...

  7. WPF资源字典使用

    资源字典出现的初衷就在于可以实现多个项目之间的共享资源,资源字典只是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情. 1.  创建资源字典 创建资源字典的过程比较简单,只 ...

  8. WPF资源字典的使用

    1.在解决方案中添加资源字典:鼠标右键——添加——资源字典 2.在资源字典中写入你需要的样式,我这里简单的写了一个窗体的边框样式 3.在App.xaml中加入刚刚新建的资源字典就好了

  9. 【WPF学习】第六十一章 组织模板资源

    为表达全国各族人民对抗击新冠肺炎疫情斗争牺牲烈士和逝世同胞的深切哀悼,国务院今天发布公告,决定2020年4月4日举行全国性哀悼活动. 当使用控件模板时,需要决定如何更广泛地共享模板,以及是否希望自动地 ...

  10. hdu 4099 Revenge of Fibonacci 字典树+大数

    将斐波那契的前100000个,每个的前40位都插入到字典树里(其他位数删掉),然后直接查询字典树就行. 此题坑点在于 1.字典树的深度不能太大,事实上,超过40在hdu就会MLE…… 2.若大数加法时 ...

随机推荐

  1. 分析三维模型OBJ格式轻量化在网络传输中的重要性

    分析三维模型OBJ格式轻量化在网络传输中的重要性 三维模型的OBJ格式轻量化在网络传输中扮演着重要的角色.随着互联网的快速发展和普及,越来越多的三维模型需要通过网络进行传输,涉及到下载.上传.共享等场 ...

  2. 记录--vue3中的ref,toRef,toRefs

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 1. ref的使用 ref 接受一个原始值,返回一个具有响应式的对象,对象有一个value属性,其值就是所传递的原始值. ref是做的一个 ...

  3. 一款超酷、功能强大的一体化网站测试工具:Web-Check

    今天给大家一款网站一体化测试工具:Web-Check! Web-Check 是一款功能强大的一体化工具,用于发现网站/主机的相关信息.用于检查网页的工具,用于确保网页的正确性和可访问性.它可以帮助开发 ...

  4. C# 按指定宽高缩放图片

    /// <summary> /// 按指定宽高缩放图片 /// </summary> /// <param name="image">原图片&l ...

  5. 什么是HSV色彩空间

    BGR色彩空间是基于三基色而言,即红色.绿色.蓝色.而HSV色彩空间则是基于色调.饱和度和亮度而言的. 色调(H)是指光的颜色,例如,彩虹中的赤,橙,黄,绿,青,蓝,紫分别表示不同的色调.在OpenC ...

  6. MySQL插入更新删除数据

    数据插入 插入完整的行 INSERT INTO customers VALUES(NULL, 'Pep E. LaPew', '100 Main Street', 'Los Angeles', 'CA ...

  7. 修复HTTP动词篡改导致的认证旁路问题的方法

    本文于2016年4月完成,发布在个人博客网站上. 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来. 诡异的问题 分析AppScan扫描报告的时候,发现报告里提示" ...

  8. protocol buffer没那么难,不信你看这篇

    目录 简介 定义一个消息 类型定义 字段的值 字段描述符 添加注释 嵌套类型 Map 总结 简介 上一篇文章我们对google的protobuf已经有了一个基本的认识,并且能够使用相应的工具生成对应的 ...

  9. [一本通1677/JZOJ1217/CJOJ1101]软件开发 题解

    题目描述 一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成\(m\)个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用 ...

  10. 深度解析 Spring Security:身份验证、授权、OAuth2 和 JWT 身份验证的完整指南

    Spring 安全框架 Spring Security 是一个用于保护基于 Java 的应用程序的框架.它是一个功能强大且高度可定制的身份验证和访问控制框架,可以轻松地集成到各种应用程序中,包括 We ...