c# - 无法在不同项目之间共享 ResourceDictionary
问题描述
ResourceDictionary
我有几个 Windows 应用程序项目,它们的app.xaml
文件中都有相同的复制粘贴。我想删除此代码重复,将ResourceDictionary
一个文件放入所有项目都引用的项目中,并使用ResourceDictionary.Source
参数来引用它。
目前,每个项目的 app.xaml 文件中都有类似的内容:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomeProject;component/SomePath/First.xaml"/>
<ResourceDictionary Source="/SomeProject;component/SomePath/Second.xaml"/>
<ResourceDictionary Source="/SomeProject;component/SomePath/Third.xaml"/>
...
</ResourceDictionary.MergedDictionaries>
所以我把它全部放在一个名为 Resources.xaml 的文件中,在一个名为Common
(为了示例的缘故)的项目中,app.xaml
我将代码更改为:
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/Common;component/Resources.xaml"/>
</Application.Resources>
当我在文件名上单击 F12 时,它会将我定向到预期的Resources.xaml
文件,但是当我启动应用程序时出现异常:
System.Windows.Markup.XamlParseException:“{DependencyProperty.UnsetValue}”不是属性“背景”的有效值。
内部异常:InvalidOperationException:“{DependencyProperty.UnsetValue}”不是属性“背景”的有效值。
我将 Resources.xaml 构建选项从“页面”更改为“资源”,但它没有改变任何东西。我还查看了这个问题,似乎我必须更改所有对 的StaticResource
引用DynamicResources
,这对我来说不是一个真正可行的解决方案。
如何防止异常?有没有其他方法可以防止这种代码重复?
解决方案
您必须使用MergedDictionaries
并使用 pack URI 方案来完全限定合并的资源。
“我有几个 Windows 应用程序项目,它们的 app.xaml 文件中都有相同的复制粘贴 ResourceDictionary。”
通常你创建一个单独的WPF APP项目并将其设置为启动项目。每个额外的项目都是类型库。这意味着它们不包含应用程序或框架入口点,这是一个派生自 的类,通常是在App.xaml和App.xaml.cs中定义Application
的部分类。Visual Studio 为WPF CustomControl Library或WPF User Control Library等控件库提供项目模板。
WPF 应用程序仅包含一个活动的App.xaml文件。如果您需要引用启动程序集以外的程序集中的资源,您可以通过在相关资源文件中定义 a 来导入它们。App
MergedDictionaries
应用程序.xaml
<Application.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/SomeProject;component/SomePath/First.xaml" />
<ResourceDictionary Source="pack://application:,,,/SomeProject;component/SomePath/Second.xaml" />
<ResourceDictionary Source="pack://application:,,,/SomeProject;component/SomePath/Third.xaml" />
...
</ResourceDictionary.MergedDictionaries>
</Application.Resources>
如果可能,建议将所有相关和共享资源移至App.xaml字典。这消除了在App.xamlMergedDictionaries
之外定义的需要,这可以提高性能。
还要确保以正确的顺序添加集合中合并ResourceDictionary
项目的顺序。MergedDictionaries
问题
请注意,XAML 解析器遵循某些查找规则。另外StaticResource
查找不支持前向声明:所有引用的资源必须在实际引用的声明之前定义。
尤其是在处理MergedDictionaries
声明的顺序时非常重要。
简而言之,静态资源查找从ResourceDictionary
当前元素的本地开始。如果在其范围内未找到资源键,XAML 解析器将向上遍历逻辑树以检查逻辑父级的字典,直到它到达根元素,例如Window
. 在根元素之后,解析器检查应用程序的资源字典,然后是主题字典。
如果解析器遇到 a MergedDictionaries
(在检查当前first 之后),它会以相反的顺序从底部到顶部或从 last 到 firstResourceDictionary
迭代合并的ResourceDictionary
集合。
由于 XAML 解析器不支持前向声明,因此合并资源的顺序非常重要。
采取以下MergedDictionaries
集合:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomePath/First.xaml" />
<ResourceDictionary Source="/SomePath/Second.xaml" />
<ResourceDictionary Source="/SomePath/Third.xaml" />
</ResourceDictionary.MergedDictionaries>
现在考虑以下情况:您有一个Button
静态引用 a的元素,例如 a ,它在Third.xamlControlTemplate
的合并字典内的父元素字典中定义。但是这个模板还包含一个元素,它静态引用First.xaml中定义的一个元素。Style
如果在Third.xaml中声明的元素或资源需要静态引用来自First.xaml的资源,则解析器无法解析这些资源:解析器搜索ControlTemplate
并到达父级的ResourceDictionary
. 这本字典不包含参考,而是一个MergedDictioanaries
集合。所以它开始以相反的顺序迭代这个集合,从最后到第一个或从下到上:它从Third.xaml开始并成功找到引用的ControlTemplate
.
为了实例化这个模板,解析器必须解析所有模板资源。在这个模板中,解析器找到了一个需要 a 的元素Style
,但Style
在任何以前的合并中都没有找到ResourceDictionary
。它在 First.xaml 中定义,ResourceDictionary
尚未访问(前向声明)。因此,无法解析此资源。
解决方案
要解决此问题,您可以将合并的字典按正确的顺序排列:
<!-- Collection is iterated in reverse order -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/SomePath/Third.xaml" />
<ResourceDictionary Source="/SomePath/Second.xaml" />
<ResourceDictionary Source="/SomePath/First.xaml" />
</ResourceDictionary.MergedDictionaries>
或者使用标记将静态引用替换为动态引用DynamicResource
。
标记指示 XAML 解析器在第DynamicResource
一次查找过程中创建一个临时表达式(第一次查找过程是前面描述的,并在编译时解析静态引用)。在这第一遍之后,在运行时进行第二次查找。解析器再次遍历树以执行先前DynamicResource
在第一次查找过程中由标记创建的临时表达式。
因此,当您无法在声明之前提供资源的定义时,您必须使用DynamicResource
查找。
推荐阅读
- xquery - Zorba 不仅仅是一个文件系统
- java - 使用带有非 ASCII 密码的 PCKS#12 证书
- javascript - 将部分html标签复制到另一个
- c++ - 范围内临时对象的生命周期是多少?
- docker - Docker:从中间层删除文件
- java - 将 bean 注册到 Spring 上下文是一种反范式吗?
- java - Observable 没有在不同的线程上运行
- api - 从 yahoo 天气 API 获取天气数据?
- android - 如何统一定义安卓游戏支持的屏幕分辨率?
- dialogflow-es - BasicCard 共享类型返回 open_url_action:在 Google Assistant Console 中协议必须是 http 或 https