大家都知道,在 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. Android组件化开发实践和案例分享

    目录介绍 1.为什么要组件化 1.1 为什么要组件化 1.2 现阶段遇到的问题 2.组件化的概念 2.1 什么是组件化 2.2 区分模块化与组件化 2.3 组件化优势好处 2.4 区分组件化和插件化 ...

  2. 【Leetcode】64. 最小路径和

    题目(链接) 给定一个包含非负整数的m x n网格grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小. 说明:每次只能向下或者向右移动一步. 示例 1: 输入:grid = [[1 ...

  3. Python 如何发送带Excel附件的邮件

    import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart f ...

  4. hdfs中acl权限管理的简单实用

    1.背景 在我们开发的过程中有这么一种场景, /projectA 目录是 hadoopdeploy用户创建的,他对这个目录有wrx权限,同时这个目录属于supergroup,在这个组中的用户也具有这个 ...

  5. 感悟:FPGA的串行及并行设计思路

    前言 FPGA设计过程中, 会遇到大量的串行转并行或者并行转串行的问题; 这些问题主要体现在FPGA对于速度和面积的均衡上; 一般而言, FPGA使用并行的设计可以提高处理的速度, 消耗更多的资源; ...

  6. List和ObservableCollection的转换

    1.我们后台查询全部List数据的时候,前台需要ObservableCollection展示 这个时候List需要转换成ObservableCollection public static Obser ...

  7. layui框架使用单页面弹出层组件layer

    layui实现单页面弹出层 首先需要导入layui的js和css: <link rel="stylesheet" href="layui/css/layui.css ...

  8. XMIND思维导图工具入门使用方法(常用操作和快捷键)

    基本操作 Tab 置入子项目 ENTER 置入平级项目 CTRL+ALT+F ZEN 专注模式 进阶操作 联系 CTRL+SHIFT+R 内容链接 概要 用括号简要概括要点[界面上部概要选项] 外框 ...

  9. #dp or 贪心+堆#CF704B Ant Man

    题目 分析(dp) 考虑到对于一个排列单独抽出 \(1\sim i\) 可能会分成若干段,而贡献一定是固定的,不会影响之后的选择. 首先 \(a,c\) 加上 \(x\),\(b,d\) 减去 \(x ...

  10. JDK14中的java tools简介

    目录 故事发生了 java tools简介 jaotc jar jarsigner java javac javadoc javap jcmd jconsole jdb jdeprscan jdeps ...