繁体   English   中英

无法在不同项目之间共享 ResourceDictionary

[英]Can't share ResourceDictionary between different projects

我有几个 Windows 应用程序项目,它们的app.xaml文件中都有相同的复制粘贴ResourceDictionary 我想删除此代码重复,将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>

因此,我将其全部放在名为Common的项目中的一个名为 Resources.xaml 的文件中(为了示例),并在app.xaml中将代码更改为:

<Application.Resources>
    <ResourceDictionary Source="pack://application:,,,/Common;component/Resources.xaml"/>
</Application.Resources>

当我在文件名上单击 F12 时,它会将我定向到预期的Resources.xaml文件,但是当我启动应用程序时,出现异常:

System.Windows.Markup.XamlParseException:“{DependencyProperty.UnsetValue}”不是属性“Background”的有效值。

内部异常:InvalidOperationException:“{DependencyProperty.UnsetValue}”不是属性“Background”的有效值。

我将 Resources.xaml 构建选项从“页面”更改为“资源”,但没有任何改变。 我还看了这个问题,似乎我必须将所有StaticResource引用更改为DynamicResources ,这对我来说不是一个真正可行的解决方案。

如何防止异常? 有没有其他方法可以防止此代码重复?

您必须使用MergedDictionaries并使用包 URI 方案来完全限定合并的资源。

“我有几个 Windows 应用程序项目,它们的 app.xaml 文件中都有相同的复制粘贴 ResourceDictionary。”

通常你创建一个单一的WPF APP项目并将其设置为启动项目。 每个额外的项目都是类型库。 这意味着它们不包含应用程序或框架入口点,这是一个派生自Application的类,通常是App.xamlApp.xaml.cs 中定义的部分类App Visual Studio 为控件库(如WPF CustomControl LibraryWPF User Control Library )提供了一个项目模板。
一个 WPF 应用程序只包含一个活动的App.xaml文件。 如果需要在启动程序集中以外的程序集中引用资源,则可以通过在相关资源文件中定义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.xaml之外定义MergedDictionaries的需要,这可以提高性能。

还要确保MergedDictionaries集合中合并的ResourceDictionary项的顺序以正确的顺序添加。


问题

请注意,XAML 解析器遵循某些查找规则。 此外, StaticResource查找不支持前向声明:所有引用的资源必须在实际引用的声明之前定义。
特别是在处理MergedDictionaries时,声明的顺序非常重要。

简而言之,静态资源查找从当前元素的ResourceDictionary本地开始。 如果在其范围内未找到资源键,则 XAML 解析器向上遍历逻辑树以检查逻辑父项的字典,直到它到达根元素,例如Window 在根元素之后,解析器检查应用程序的资源字典,然后是主题字典。

如果解析器遇到MergedDictionaries (在首先检查当前ResourceDictionary之后),它会以相反的顺序从下到上或从最后到第一个迭代合并的ResourceDictionary集合。

由于 XAML 解析器不支持前向声明,因此合并资源的顺序非常重要。
采用以下MergedDictionaries集合:

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="/SomePath/First.xaml" />
  <ResourceDictionary Source="/SomePath/Second.xaml" />
  <ResourceDictionary Source="/SomePath/Third.xaml" />
</ResourceDictionary.MergedDictionaries>

现在考虑以下情况:您有一个元素,例如静态引用ControlTemplateButton ,该ControlTemplate是在Third.xaml的合并字典内的父元素的字典中定义的 但这种模板还包含一个元素,即静态引用StyleFirst.xaml定义。

如果在Third.xaml 中声明的元素或资源需要从First.xaml静态引用资源,则解析器无法解析这些资源:解析器搜索ControlTemplate并到达父级的ResourceDictionary 这本字典不包含参考,而是一个MergedDictioanaries集合。 所以它开始以相反的顺序迭代这个集合,从最后到第一个或从下到上:它从Third.xaml开始并成功找到引用的ControlTemplate

为了实例化这个模板,解析器必须解析所有模板资源。 在这个模板中,解析器找到一个需要Style的元素,但在任何先前合并的ResourceDictionary都没有找到这个Style 它在First.xamlResourceDictionary定义,尚未访问(前向声明)。 因此无法解析此资源。

解决方案

要解决此问题,您可以将合并的字典按正确的顺序排列:

<!-- 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标记将静态引用替换为动态引用。

DynamicResource标记指示 XAML 解析器在第一次查找过程中创建一个临时表达式(这个第一次查找过程是前面描述的,并在编译时解析静态引用)。 在第一遍之后,在运行时进行第二次查找。 解析器再次遍历树以执行先前在第一次查找过程中由DynamicResource标记创建的临时表达式。

因此,只要您不能声明之前提供资源的定义,就必须使用DynamicResource查找。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM