前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >WPF 已知问题 资源字典树引用与资源寻找的坑

WPF 已知问题 资源字典树引用与资源寻找的坑

作者头像
林德熙
发布于 2022-08-12 11:42:33
发布于 2022-08-12 11:42:33
89500
代码可运行
举报
文章被收录于专栏:林德熙的博客林德熙的博客
运行总次数:0
代码可运行

大家都知道,在 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 里面作为共用资源,定义了画刷资源,如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 资源,代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 里面的合入代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 里面需要引用资源时找不到资源

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
System.Windows.Markup.XamlParseException:““{DependencyProperty.UnsetValue}”不是属性“Background”的有效值。”

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 6f0ed747acf089095a8503bc8ff967c97efe9de5

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 的代码变更如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 定义了资源

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 资源

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 两个资源字典,代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 里面定义资源

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 资源,代码和上文的一样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 66820e750fb1b5a104b3b4582dd31ac7393439bb

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 的代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WPF 将 StaticResource 和 ResourceDictionary 放在一起的魔幻行为
本文将记录一些在 WPF 里面,使用 StaticResource 将 ResourceDictionary 玩坏的做法。大家可以放心的是,这些玩法基本只有高级玩家或逗比开发者才会使用到
林德熙
2023/06/23
7440
win10 uwp 资源字典 资源的key所有的元素都可以定义资源合并资源字典主题资源共享的资源用户控件资源定义
本文主要翻译ResourceDictionary and XAML resource references - UWP app developer ,里面的代码我重新写了一下,有一些不相同。
林德熙
2018/09/18
1.1K0
WPF 给类库设置设计时使用的资源字典
在开发 WPF 类库时,由于类库里面没有存在 App.xaml.cs 文件,而在对单个 XAML 进行开发时,设计器将会因为找不到资源文件的存在,而拿不到资源。本文告诉大家简单的方法,给设计器设置仅在设计时引用的资源
林德熙
2021/04/25
2.2K0
WPF 给类库设置设计时使用的资源字典
WPF基础之资源
WPF资源系统是一种保管一系列对象(如常用的画刷、样式或模版)的简单办法,从而使您更容易地复用这些对象。
zls365
2020/12/29
8060
WPF XAML 为项目设置全局样式
正确的做法是封装统一风格的所有控件。 (例如按钮,统一高宽,字体,字体大小,然后申明到独立的资源字典中, 在App.xaml中引用)
zls365
2021/10/19
1.8K0
WPF 在后台代码定义 ResourceDictionary 资源字典
在 WPF 中的 ResourceDictionary 资源字典大部分都是在 XAML 里面定义的,但是在 C# 代码定义一个资源字典也是可行的,只是写起来有点诡异
林德熙
2022/08/12
1.4K0
[WPF]本地化入门
WPF的本地化是个很常见的功能,我做过的WPF程序大部分都实现了本地化(不管最终有没有用到)。通常本地化有以下几点需求:
dino.c
2019/01/18
2.5K0
[WPF]本地化入门
C# WPF UI框架MahApps切换主题
本指南将向您介绍MahApps.Metro如何切换主题,所有的MahApps.Metro的主题都包含在单独的资源字典中。
用户9127601
2022/06/09
1.1K0
C# WPF UI框架MahApps切换主题
C# WPF MVVM开发框架Caliburn.Micro快速搭建③
既然Caliburn.Micro更喜欢ViewModel优先的方法,让我们从这里开始。
用户9127601
2022/01/13
1.6K0
C# WPF MVVM开发框架Caliburn.Micro快速搭建③
UWP开发01之Windows UI2.x
官方地址:https://docs.microsoft.com/zh-cn/windows/apps/winui/winui2/
码客说
2020/08/20
8190
.NET 9 中为 WPF 新增的功能
本文介绍适用于 .NET 9 的 Windows Presentation Foundation (WPF) 中的新增功能。今年 WPF 的主要关注领域是改进 WPF 的视觉功能,并为 Windows 11 提供基于 Fluent 设计原则的新主题。
独立观察员
2024/11/23
1110
.NET 9 中为 WPF 新增的功能
少量代码设计一个登录界面 - .NET CORE(C#) WPF开发
继续 MaterialDesignThemes 开源控件库学习,本文简单使用输入控件的水印附加属性:materialDesign:HintAssist.Hint。
zls365
2020/12/29
1.6K0
少量代码设计一个登录界面 - .NET CORE(C#) WPF开发
.NET CORE(C#) WPF亚克力窗体
使用 .Net Core 3.1 创建名为 “AcrylicWindow” 的WPF模板项目,添加三个Nuget库:MaterialDesignThemes、MaterialDesignColors和FluentWPF,其中亚克力效果是由FluentWPF控件库实现的。
沙漠尽头的狼
2020/01/16
2.3K0
WPF启动优化
随着项目越来越复杂,我们可能会在App中添加很多逻辑,这些逻辑可能还要保证在窗口加载前执行,这样就导致在配置相对较差的电脑上,用户看到第一个界面的时间就会较长,体验不好,怎样解决呢。
码客说
2023/11/23
2160
桌面程序用什么语言开发好[安卓开发用什么语言]
促使程序赢得更多客户的最好、最经济的方法是使之支持多国语言,而不是将潜在的客户群限制为全球近70亿人口中的一小部分。本文介绍四种实现WPF应用程序支持多国语言的解决方案。
Java架构师必看
2022/02/28
1.8K0
桌面程序用什么语言开发好[安卓开发用什么语言]
轻量级MVVM框架Stylet介绍:(3)关于Bootstrapper「建议收藏」
Bootstrapper负责引导应用程序,用于配置 IoC 容器,创建根 ViewModel 的新实例,并使用显示WindowManager出来。它还提供了各种其他功能,如下所述。
全栈程序员站长
2022/09/06
8130
少量代码设计一个登录界面(二) – .NET CORE(C#) WPF开发
使用 .NET CORE 3.1 创建名为 “Login” 的WPF模板项目,添加1个Nuget库:MaterialDesignThemes.3.1.0-ci981。
zls365
2020/12/29
1.5K0
少量代码设计一个登录界面(二) – .NET CORE(C#) WPF开发
win10 uwp 使用资源在后台创建控件
本文告诉大家如何使用资源在后台创建控件,本文使用按钮做例子,包括如何绑定资源,找到资源。
林德熙
2018/09/19
6560
win10 uwp 使用资源在后台创建控件
C# WPF框架Caliburn.Micro入门实例1
详细介绍了一个最简单的Caliburn.Micro框架如何搭建起来,今天我们接着上次的话题继续讲解。
用户9127601
2021/11/01
7340
【愚公系列】2023年02月 .NET CORE工具案例-Caliburn.Micro的使用基于WPF的改造的MVVM案例
Caliburn.Micro是一个微软开发的用于构建WPF,Silverlight和Windows Phone应用程序的MVVM(模型-视图-视图模型)框架。它提供了一系列的工具和类,帮助开发人员更快,更轻松地构建美观的和可维护的应用程序。
愚公搬代码
2023/03/16
1.1K0
【愚公系列】2023年02月 .NET CORE工具案例-Caliburn.Micro的使用基于WPF的改造的MVVM案例
相关推荐
WPF 将 StaticResource 和 ResourceDictionary 放在一起的魔幻行为
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文