[英]Is it possible to share a ResourceDictionary file between multiple projects?
[英]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.xaml和App.xaml.cs 中定义的部分类App
。 Visual Studio 为控件库(如WPF CustomControl Library或WPF 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>
现在考虑以下情况:您有一个元素,例如静态引用ControlTemplate
的Button
,该ControlTemplate
是在Third.xaml的合并字典内的父元素的字典中定义的。 但这种模板还包含一个元素,即静态引用Style
在First.xaml定义。
如果在Third.xaml 中声明的元素或资源需要从First.xaml静态引用资源,则解析器无法解析这些资源:解析器搜索ControlTemplate
并到达父级的ResourceDictionary
。 这本字典不包含参考,而是一个MergedDictioanaries
集合。 所以它开始以相反的顺序迭代这个集合,从最后到第一个或从下到上:它从Third.xaml开始并成功找到引用的ControlTemplate
。
为了实例化这个模板,解析器必须解析所有模板资源。 在这个模板中,解析器找到一个需要Style
的元素,但在任何先前合并的ResourceDictionary
都没有找到这个Style
。 它在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
标记将静态引用替换为动态引用。
DynamicResource
标记指示 XAML 解析器在第一次查找过程中创建一个临时表达式(这个第一次查找过程是前面描述的,并在编译时解析静态引用)。 在第一遍之后,在运行时进行第二次查找。 解析器再次遍历树以执行先前在第一次查找过程中由DynamicResource
标记创建的临时表达式。
因此,只要您不能在声明之前提供资源的定义,就必须使用DynamicResource
查找。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.