本文将记录一些在 WPF 里面,使用 StaticResource 将 ResourceDictionary 玩坏的做法。大家可以放心的是,这些玩法基本只有高级玩家或逗比开发者才会使用到

后加入的资源无法被 StaticResource 找到

在 App.xaml.cs 后台代码里面,手动加入资源字典,手动加入的资源字典包含的资源,无法被提前在 App.xaml 加入的资源里面的 StaticResource 找到

测试方式如下

定义两个资源字典,分别是 Dictionary1.xaml 和 Dictionary2.xaml 字典。在 Dictionary1 里定义资源,在 Dictionary2 使用 StaticResource 引用 Dictionary1 的资源。在 App.xaml 引用 Dictionary2.xaml 字典,在 App.xaml.cs 加入 Dictionary1.xaml 字典。此时运行将会发现 Dictionary2 里使用 StaticResource 的属性的值是 DependencyProperty.UnsetValue 值,表示找不到资源

细节的步骤如下

定义两个资源字典,分别是 Dictionary1.xaml 和 Dictionary2.xaml 字典

在 Dictionary1.xaml 里面定义资源,如以下代码

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

在 Dictionary2 使用 StaticResource 引用 Dictionary1 的资源,如以下代码

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="RectangleStyle" TargetType="Rectangle">
<Setter Property="Fill" Value="{StaticResource SolidColorBrush}"></Setter>
</Style>
</ResourceDictionary>

在 App.xaml 里只引用 Dictionary2.xaml 字典,如以下代码

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

在 App.xaml.cs 加入 Dictionary1.xaml 字典

public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
var resourceDictionary = new ResourceDictionary()
{
Source = new Uri("/Dictionary1.xaml", UriKind.RelativeOrAbsolute)
}; Resources.MergedDictionaries.Add(resourceDictionary); base.OnStartup(e);
}
}

接着在 MainWindow.xaml 使用 Dictionary2.xaml 定义的资源,如以下代码

<Window x:Class="JayabawwiWhenenearfajay.MainWindow"
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"
xmlns:local="clr-namespace:JayabawwiWhenenearfajay"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Rectangle Style="{StaticResource RectangleStyle}"></Rectangle>
</Grid>
</Window>

运行程序,将提示以下代码

System.InvalidOperationException:““{DependencyProperty.UnsetValue}”不是属性“Fill”的有效值。”

这就证明了定义在 Dictionary2.xaml 的 RectangleStyle 里的 Fill 属性找不到资源。也就是 Setter Property="Fill" Value="{StaticResource SolidColorBrush}" 这里的 StaticResource 无法找到定义在 Dictionary1.xaml 的资源

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

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

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

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

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

自定义 ResourceDictionary 资源可影响 StaticResource 寻找策略

以上的例子看起来还算正常,接下来来点魔幻的玩法

测试方式如下

在后台代码定义继承 ResourceDictionary 的类型,在此类型里面定义好和 Dictionary1.xaml 里的资源重名的资源,此时 Dictionary2.xaml 的 StaticResource 在运行将找对资源

也就是经过一番玩法,居然发现 StaticResource 又从 Dictionary1.xaml 里找对资源了

细节的步骤如下

在上一个例子的项目前提下,再新建一个名为 FooResourceDictionary 的类型,在构造函数添加上和 Dictionary1.xaml 里的资源重名的资源,代码如下

public class FooResourceDictionary : ResourceDictionary
{
public FooResourceDictionary()
{
Add("SolidColorBrush", this);
} protected override void OnGettingValue(object key, ref object value, out bool canCache)
{
Debug.WriteLine(key);
base.OnGettingValue(key, ref value, out canCache);
}
}

以上代码在构造函数特别有趣的加入了 "SolidColorBrush" 资源,且设置资源的 Value 是 this 值。这就意味着如果 StaticResource 直接使用 FooResourceDictionary 里的 "SolidColorBrush" 资源,将拿到 FooResourceDictionary 类型的资源,完全无法转换为 Brush 类型,将会失败。然而实际上有趣的是最终 StaticResource 还是能找对资源

以上代码为了方便调试,也重写了 OnGettingValue 方法,这个方法是为了后文的另一个魔幻行为。不重写也不会影响当前的例子的行为

接着将这个自定义的 FooResourceDictionary 类型加入到 App.xaml 里面,必须放在 Dictionary2.xaml 之前,如以下代码

<Application x:Class="JayabawwiWhenenearfajay.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:JayabawwiWhenenearfajay"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- <ResourceDictionary Source="Dictionary1.xaml"></ResourceDictionary> -->
<local:FooResourceDictionary/>
<ResourceDictionary Source="Dictionary2.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

接着依然是在 App.xaml.cs 里面加入 Dictionary1.xaml 资源,代码和之前的完全相同,没有做任何改动。同样的 MainWindow.xaml 里面也没有做任何的改动

运行代码,可以看到这一次执行正常,静态资源寻找到了定义在 Dictionary1.xaml 的资源,不会受到在 FooResourceDictionary 定义的影响

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

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

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

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

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

通过以上的两个例子可以说明 StaticResource 的行为是在资源加载的过程中就会执行,执行时将会尝试从资源字典里寻找静态资源 Key 的定义,如果有找到 Key 的记录,则加入延迟初始化逻辑。延迟初始化逻辑还没有绑定到具体哪个资源字典,是在实际需要获取值的时候,才进行重新确定实际的资源。这也就是为什么 FooResourceDictionary 的 OnGettingValue 方法没有进入的原因,因为 StaticResource 实际获取值是从 Dictionary1.xaml 获取的,完全不在 FooResourceDictionary 里获取

如果没有找到 Key 的记录,那就直接给属性赋值为 DependencyProperty.UnsetValue 属性,结束寻找。即使后续加入的资源字典添加了对应的资源,也不会重新更新。这个行为符合微软的文档,试试看交换两个有依赖关系的资源字典加入 App.xaml 的顺序,可以看到顺序倒了之后将导致静态资源找不到。这个行为和资源字典加入顺序导致的找不到资源是相同的

在此例子里面是通过在 FooResourceDictionary 的构造里面,构建了 "SolidColorBrush" 资源,从而让 StaticResource 静态绑定资源引用设置给属性一个延迟初始化值,在实际的界面使用时,获取到 Dictionary1.xaml 覆盖 FooResourceDictionary 的资源

有些资源如果想要延迟加入到 App.xaml 里面,延迟初始化资源字典的话,就需要考虑 StaticResource 寻找资源的问题。一个可选的方式是自己定义继承 ResourceDictionary 的类型,如本文的 FooResourceDictionary 类型,在类型的构造函数里面写满了 StaticResource 可能使用的资源,从而让 StaticResource 加入延迟初始化逻辑

在后台代码加入新资源字典之前读取静态资源引用的值

上一个例子可以正确获取到资源,在上一个例子的基础上,后台代码加入 Dictionary1.xaml 之前,尝试获取 StaticResource 静态绑定资源引用的值。获取到的值,可以看到获取到的是定义在 FooResourceDictionary 里的资源,很符合预期。但有趣的是,之后尽管加入了 Dictionary1.xaml 但静态资源引用的值不会更新,应用无法跑起来,将提示以下代码

System.InvalidOperationException:““JayabawwiWhenenearfajay.FooResourceDictionary”不是属性“Fill”的有效值。”

详细的步骤如下

只在 App.xaml.cs 的 Dictionary1.xaml 加入之前,添加以下代码用来获取静态绑定资源引用属性的值

        var value = ((System.Windows.Setter) (Resources.MergedDictionaries)[1].Values.OfType<Style>().First().Setters[0]).Value;

修改之后的代码如下

public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// 注释这句话试试
var value = ((System.Windows.Setter) (Resources.MergedDictionaries)[1].Values.OfType<Style>().First().Setters[0]).Value; var resourceDictionary = new ResourceDictionary()
{
Source = new Uri("/Dictionary1.xaml", UriKind.RelativeOrAbsolute)
}; Resources.MergedDictionaries.Add(resourceDictionary); base.OnStartup(e);
}
}

这里可以看到 value 获取时,将进入 FooResourceDictionary 的 OnGettingValue 函数。拿到的 value 是 FooResourceDictionary 类型,也就是这个资源是在 FooResourceDictionary 提供的。符合预期,因为此时 Dictionary1.xaml 还没加入

但有趣的是在应用运行的时候,即使 Dictionary1.xaml 已经加入,此时拿到的还是原来的 FooResourceDictionary 类型,从而运行失败

这个行为不算魔幻,这是因为 StaticResource 只执行一次,即使后续的字典变更了,也不会重新执行。这是 StaticResource 和 DynamicResource 的差别,这也就是使用 StaticResource 时性能更高的原因。以上的代码在 Dictionary1.xaml 加入之前,获取 StaticResource 静态资源引用绑定的属性的值,从而让 StaticResource 执行,找到了在 FooResourceDictionary 定义的资源。由于 StaticResource 只执行一次,这就导致了即使后续加入 Dictionary1.xaml 资源字典,也不会更新 StaticResource 静态资源引用绑定的属性的值为 Dictionary1.xaml 资源字典的资源,于是应用程序就拿到了错误的对象放入 Fill 属性,运行失败

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

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

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

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

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

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

资源字典树引用与资源寻找的坑

详细请参阅 WPF 已知问题 资源字典树引用与资源寻找的坑

WPF 将 StaticResource 和 ResourceDictionary 放在一起的魔幻行为的更多相关文章

  1. WPF 绑定StaticResource到控件的方法

    原文:WPF 绑定StaticResource到控件的方法 资源文件内的属性能否直接通过绑定应用到控件?答案是肯定的. 比如,我们要直接把下面的<SolidColorBrush x:Key=&q ...

  2. WPF当中StaticResource调用方法

    1.先在Converter命名空间当中,定义转换功能类: public sealed class BoolToValueConverter : System.Windows.Data.IValueCo ...

  3. 《深入浅出WPF》读书笔记

    依赖属性: 节省实例对内存的开销: 属性值可以通过Binding依赖到其他对象上. WPF中,依赖对象的概念被DependencyObject类实现,依赖属性被DependencyProperty类实 ...

  4. WPF之神奇的资源

    原文:WPF之神奇的资源 WPF中的资源有两种,一种称为"程序集资源"(assembly resource),另一种称为"对象资源"(object resour ...

  5. WPF学习(9)样式和行为

    在asp.net世界中,我们的美工人员会为我们准备好静态页面,它注意包括三个部分:html.css和js.而在WPF世界里,也同样有着类似这三个部分的静态页面:Xaml.Style和Behaviors ...

  6. 【WPF】城市级联(XmlDataProvider)

    首先在绑定的时候进行转换: public class RegionConverter : IValueConverter { public object Convert(object value, T ...

  7. C# WPF可拖拽的TabControl

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. C# WPF可拖拽的TabControl 阅读导航 本文背景 代码实现 本文参考 源码 1. ...

  8. 《Programming WPF》翻译 第7章 3.笔刷和钢笔

    原文:<Programming WPF>翻译 第7章 3.笔刷和钢笔 为了在屏幕上绘制一个图形,WPF需要知道你想要为图形填充什么颜色以及如何绘制它的边框.WPF提供了一些Brush类型支 ...

  9. WPF学习(10)模板

    在前面一篇我们粗略说了Style和Behaviors,如果要自定义一个个性十足的控件,仅仅用Style和Behaviors是不行的,Style和Behaviors只能通过控件的既有属性来简单改变外观, ...

  10. WPF 学习笔记 路由事件

    1. 可传递的消息: WPF的UI是由布局组建和控件构成的树形结构,当这棵树上的某个节点激发出某个事件时,程序员可以选择以传统的直接事件模式让响应者来响应之,也可以让这个事件在UI组件树沿着一定的方向 ...

随机推荐

  1. 无力吐槽,uniapp项目外包接单碰上了这样的开发人员

    随着疫情期形式的不断变化,线上线下各个行业都受到冲击.2020年我在家歇息了近4个月:跟很多人一样这一年我失业了,那段时间真的很迷茫,年龄也已过30,加上网上各种"开发人员35岁" ...

  2. MySQL系列:索引失效场景总结

    相关文章 数据库系列:MySQL慢查询分析和性能优化 数据库系列:MySQL索引优化总结(综合版) 数据库系列:高并发下的数据字段变更 数据库系列:覆盖索引和规避回表 数据库系列:数据库高可用及无损扩 ...

  3. 记录--这个前端Api管理方案会更好?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 简介 大家好,前端小白一枚,目前接触后台管理系统比较多,经常遇到不同对象的增删改查的接口,如何对Api进行一个有比较好的管理是个问题.在学 ...

  4. 工具推荐-sourcetree

    工具推荐-sourcetree 简介 简单好用的win系统下的git可视化软件 支持ssh免密登录 一键暂存和上传到开源仓库 开源免费 安装 下载sourcetree sourcetree下载 下载g ...

  5. java实战字符串2:中英字符串相互转换

    题目描述 输入字符串为中文拼音号码串或者英文号码串,如果输入是中文拼音号码串则转成英文号码串,如果输入是英文号码串则转成中文号码串. 特殊情况是英文号码串会出现Double + 英文数字或者拼音数值. ...

  6. #并查集,线性筛#nssl 1470 X

    分析 显然答案就是\(2^{连通块个数}-2\), 将每个数的质数所在的集合合并, 最后判断连通块个数即可(线性筛少了个等号改了半天QWQ) 代码 #include <cstdio> #i ...

  7. #矩阵乘法,线段树#CF575A Fibonotci

    题目 分析 \(K\)那么大肯定是矩阵乘法, 带修改可以用线段树单点修改, 转移矩阵类似于斐波那契数列, 这题思维难度不大,细节很多,需要很长时间QWQ 时间复杂度\(O(mlog_2K)\),具体注 ...

  8. #搜索#CF525D Arthur and Walls

    题目 给出一个\(n*m\)的矩阵,里面有""和"."两种符号,要求把最少的""变成".", 使得".&quo ...

  9. web.xml最新配置文件

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmln ...

  10. AI云增强升级!还原生动人像,拍出质感照片

    近期不少细心用户发现,在用HUAWEI Mate 60 Pro手机拍照后,使用相册中的AI云增强功能,照片变得更加细腻有质感.这是因为AI云增强升级并更新支持了人像模式拍摄的照片,高清自然的人像细节还 ...