无法在不同项目之间共享 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>

所以我把它全部放在一个名为 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}' is not a valid value for property 'Background'.'

Inner Exception: InvalidOperationException: '{DependencyProperty.UnsetValue}' is not a valid value for property 'Background'.

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

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

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

"I have several Windows application projects that all have the same copy-pasted ResourceDictionary in their app.xaml file."

通常您会创建一个 WPF APP 项目并将其设置为启动项目。每个附加项目都是类型库。这意味着它们不包含应用程序或框架入口点,这是从 Application 派生的 class,通常是 [=80= 中定义的部分 class App ]App.xaml 和 App.xaml.cs。 Visual Studio 为 WPF CustomControl LibraryWPF User Control Library.
等控件库提供项目模板 一个 WPF 应用程序只包含一个活动的 App.xaml 文件。如果需要引用启动程序集以外的程序集中的资源,则通过在相关资源文件中定义MergedDictionaries来导入它们。

App.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,它在 Third.xaml 的合并字典内的父元素字典中定义。但是此模板还包含一个元素,该元素静态引用 First.xaml.

中定义的 Style

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

为了实例化此模板,解析器必须解析所有模板资源。在这个模板中,解析器找到了一个需要 Style 的元素,但是这个 Style 在任何之前合并的 ResourceDictionary 中都没有找到。定义在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查找。