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.若大数加法时 ...
随机推荐
- 记录--Vue3基于Grid布局简单实现一个瀑布流组件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 在学习Grid布局之时,我发现其是CSS中的一种强大的布局方案,它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局 ...
- 记录--uni-app实现蓝牙打印小票
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 说明 基于uni-app开发,调用官方蓝牙相关api实现连接蓝牙与向蓝牙热敏打印机发送字节流,可打印文字,二维码,图片,调整字体大小等,本 ...
- Lambda表达式编写递归函数
class Program { //Fix求出的是函数f的不动点,它就是我们所需要的递归函数: static Func<T, TResult> Fix<T, TResult>( ...
- vivado2019操作之约束文件
Vivado2019的约束文件 1. 约束文件 vivado的约束文件是以xdc为后缀的.该文件具有时序约束和管脚约束的作用.该文件可以自己创建,也可以通过内置工具创建. 2.基本操作 (1)使用内部 ...
- 【已解决】ERROR: but there is no HDFS_NAMENODE_USER defined. Aborting operation. Starting datanodes
export HDFS_NAMENODE_USER=rootexport HDFS_DATANODE_USER=rootexport HDFS_SECONDARYNAMENODE_USER=roote ...
- #Multi-SG#Poj 3537 Crosses and Crosses
题目 有\(n\)个格子,可以在上面涂黑,连续三个黑色获胜,问先手是否必胜 分析 如果先手选择第\(i\)个格子涂黑,那么后手对于\(i-1,i+1,i-2,i+2\)一旦涂黑必败, 所以如果第\(i ...
- #错排,高精度#洛谷 3182 [HAOI2016]放棋子
题目 分析 这题目太迷惑人了: 每行有一个障碍,数据保证任意两个障碍不在同一行,任意两个障碍不在同一列 \(n\) 个棋子也满足每行只有一枚棋子,每列只有一枚棋子 仔细想想会发现求的是错排方案,那也就 ...
- #环#nssl 1487 图
题目 在一个\(n\)个节点\(n\)条边的连通图中, 每条边的权值为两个端点的权值的和. 已知各边权值,求各点权值 (保证环的大小一定是奇数) 分析 考虑断掉环的某一条边,设根节点的答案为\(ax+ ...
- 两个专栏帮你搞定【图像拼接(image stitching)】
[图像拼接论文精读]专栏:图像拼接论文精读 [图像拼接源码精读]专栏:图像拼接论文源码精读 前言 图像拼接(image stitching)是计算机视觉中的高级图像处理手段,是一个小众方向,研究的人很 ...
- DEB打包教程
一.deb简介 deb是一种安装包的格式,linux上常见的安装包主要是deb.rpm 二.deb简单使用 # deb安装 sudo dpkg -i webcamera_1.0_amd64.deb # ...