WPF 已知问题 资源字典树引用与资源寻找的坑
大家都知道,在 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”的有效值。”
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 的资源。更新之后的代码放在 github 和 gitee 欢迎访问
那如果继续让 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 字典树上的资源,是可以相互寻找到的
同理,再次提升层级进行测试,结果依然是能找到资源的。再定义 DictionaryE.xaml 和 DictionaryF.xaml 资源字典,让 DictionaryE.xaml 去引用 DictionaryF.xaml 的资源,其引用关系如下

通过以上的测试可以了解到,在去掉 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 资源
大家可以尝试一下这个更新之后的代码,更新之后的代码放在 github 和 gitee 欢迎访问
可以通过如下方式获取更新后代码,先创建一个空文件夹,接着使用命令行 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 已知问题 资源字典树引用与资源寻找的坑的更多相关文章
- wpf资源嵌套,一个资源引用另外一个资源,被引用的资源应该声明在前面
在wpf的XAML的Window.Resources中,一个资源引用另外一个资源,出现如下错误: “错误 1 “{DependencyProperty.UnsetValue}”不是 Setter 上“ ...
- (转载)资源字典(Pro WPF 学习)
原地址:http://www.cnblogs.com/yxhq/archive/2012/07/09/2582508.html 1.创建资源字典 下面是一个资源字典(AppBrushes.xaml), ...
- WPF之资源字典zz
最近在看wpf相关东西,虽然有过两年的wpf方面的开发经验,但是当时开发的时候,许多东西一知半解,至今都是模模糊糊,框架基本是别人搭建,自己也就照着模板写写,现在许多东西慢慢的理解了,回顾以前的若干记 ...
- WPF学习笔记-使用自定义资源字典(style)文件
1.添加资源字典文件style.xmal 2.在资源字典中添加自定义style等 <ResourceDictionary xmlns="http://schemas.microsoft ...
- WPF使用资源字典组织资源
转载:http://blog.163.com/wangzhenguo2005@126/blog/static/371405262010111413321728/ 首先在解决方案资源管理器中添加 ...
- WPF资源字典的使用【转】
资源字典出现的初衷就在于可以实现多个项目之间的共享资源,资源字典只是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情. 1. 创建资源字典 创建资源字典的过程比较简单,只 ...
- WPF资源字典使用
资源字典出现的初衷就在于可以实现多个项目之间的共享资源,资源字典只是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情. 1. 创建资源字典 创建资源字典的过程比较简单,只 ...
- WPF资源字典的使用
1.在解决方案中添加资源字典:鼠标右键——添加——资源字典 2.在资源字典中写入你需要的样式,我这里简单的写了一个窗体的边框样式 3.在App.xaml中加入刚刚新建的资源字典就好了
- 【WPF学习】第六十一章 组织模板资源
为表达全国各族人民对抗击新冠肺炎疫情斗争牺牲烈士和逝世同胞的深切哀悼,国务院今天发布公告,决定2020年4月4日举行全国性哀悼活动. 当使用控件模板时,需要决定如何更广泛地共享模板,以及是否希望自动地 ...
- hdu 4099 Revenge of Fibonacci 字典树+大数
将斐波那契的前100000个,每个的前40位都插入到字典树里(其他位数删掉),然后直接查询字典树就行. 此题坑点在于 1.字典树的深度不能太大,事实上,超过40在hdu就会MLE…… 2.若大数加法时 ...
随机推荐
- 使用JMeter从JSON响应的URL参数中提取特定值
在使用Apache JMeter进行API测试时,我们经常需要从JSON格式的响应中提取特定字段的值.这可以通过使用JMeter内置的JSON提取器和正则表达式提取器来完成.以下是一个具体的例子,展示 ...
- Saltstack 最大打开文件数问题之奇怪的 8192
哈喽大家好,我是咸鱼. 今天分享一个在压测过程中遇到的问题,当时排查这个问题费了我们好大的劲,所以我觉得有必要写一篇文章来记录一下. 问题出现 周末在进行压测的时候,测试和开发的同事反映压测有问题,请 ...
- 鸿蒙HarmonyOS实战-ArkUI组件(Flex)
一.Flex 1.概述 Flex布局它可以让容器中的子元素具有弹性伸缩性.Flex布局是一种二维布局模型,它可以在任意方向上对元素进行排列,并且可以动态地调整元素的大小和位置,以适应不同的屏幕尺寸和设 ...
- Unity实现敌人生命条
在敌人物体身上添加 Slider,将Background设置为黑色,FIllarea设置为绿色,调整滑块大小. 生命值减少代码设计如下: using System.Collections; using ...
- GID:旷视提出全方位的检测模型知识蒸馏 | CVPR 2021
论文提出的GID框架能够自动选择可辨别目标用于知识蒸馏,而且综合了feature-based.relation-based和response-based知识,全方位蒸馏,适用于不同的检测框架中.从实验 ...
- python 1992和2006年国家标准学科分类和代码标准化并存入MySQL数据库
数据表 代码 1 import pandas as pd 2 import pymysql 3 4 5 def get_subject_1992(): 6 res={} 7 the_former_co ...
- 初识urllib与requests
urllib与requests 一.urllib的学习 学习目标 了解urllib的基本使用 1.urllib介绍 除了requests模块可以发送请求之外, urllib模块也可以实现请求的发送,只 ...
- 02 jQuery选择器
02 jQuery选择器 jQuery的逻辑和css选择器的逻辑是一样的. // 语法: $(选择器) 可以使用jQuery选择器快速的对页面结构进行操作. 案例: <!DOCTYPE html ...
- #轮廓线dp,博弈论#洛谷 4363 [九省联考 2018] 一双木棋 chess
题目传送门 分析 菲菲想让答案尽量大,牛牛想让答案尽量小. 很天真的一种想法就是设 \(dp[i][j]\) 表示现在选择 \((i,j)\) 的答案. 但是这样有一个弊端就是并不知道其它位置怎么选择 ...
- 实践指南:EdgeOne与HAI的梦幻联动
在当今快速发展的数字时代,安全和速度已成为网络服务的基石.EdgeOne,作为腾讯云提供的边缘安全加速平台,以其全球部署的节点和强大的安全防护功能,为用户提供了稳定而高效的网络体验.而HAI(Hype ...