引用包含合并字典的资源字典出现问题

44

我有一个名为CommonLibraryWpfThemes的库,其中包含多个Resource Dictionary XAML文件。我的Themes/Generic.xml文件包含一个ResourceDictionary.MergedDictionaries声明,将所有其他文件合并在一起。

Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary
            Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/BrushDictionary.xaml" />
        <ResourceDictionary
            Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/TextBlockDictionary.xaml" />
        <ResourceDictionary
            Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/LabelDictionary.xaml" />
        <ResourceDictionary
            Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/ButtonDictionary.xaml" />
        <ResourceDictionary
            Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/WindowDictionary.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

在我的应用程序项目中,我引用了CommonLibraryWpfThemes,并且在App.xaml文件中明确引用了Generic.xml。

App.xaml -- 失败

<Application
    x:Class="MyApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary
            Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
    </Application.Resources>
</Application>

这个不起作用。 我运行我的应用程序时收到以下错误:

System.Windows.Markup.XamlParseException occurred
  Message="Cannot find resource named '{_fadedOrangeBrush}'. Resource names are case sensitive.  Error at object 'System.Windows.Setter' in markup file 'CommonLibraryWpfThemes;component/ResourceDictionaries/WindowDictionary.xaml' Line 18 Position 13."
  Source="PresentationFramework"
  LineNumber=18
  LinePosition=13
如果我直接将Generic.xaml的内容放入App.xaml中,一切都能正常工作: App.xaml -- 成功
<Application
    x:Class="MyApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary
                    Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/BrushDictionary.xaml" />
                <ResourceDictionary
                    Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/TextBlockDictionary.xaml" />
                <ResourceDictionary
                    Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/LabelDictionary.xaml" />
                <ResourceDictionary
                    Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/ButtonDictionary.xaml" />
                <ResourceDictionary
                    Source="/CommonLibraryWpfThemes;component/ResourceDictionaries/WindowDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
也许我走错了方向。我的目标是使多个应用程序能够轻松引用所有主题资源,而不必列出所有单独的文件。有没有推荐的方法?(注意:我不想在多个主题之间切换-我只有一个主题。)
作为奖励,如果有人能告诉我如何引用外部库中的资源而不会破坏Visual Studio中的设计器,则会很好。
谢谢。
编辑:
我尝试将 ResourceDictionary 包装在 ResourceDictionary.MergedDictionary 元素中,但这也没有起作用(我得到相同的错误)。
<Application
    x:Class="MyApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary
                    Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

1
我会在这里设立一个小赏金。将几个RessourceDictionaries合并到一个主题中(然后只需在App.xaml中加载它)似乎是一个相当常见的场景... - Jens
5个回答

68

之前在这里回答过类似的问题,请参见添加合并字典到合并字典中的问题。

这是一个优化错误,请参见Microsoft Connect / DefaultStyleKey style not found in inner MergedDictionaries

在XAML中创建每个对象时,如果存在默认样式(即带有Type键的样式),则应用该样式。正如您所想象的那样,有几个性能优化可以使该(隐含)查找尽可能轻量级。其中之一是,除非它们被标记为“包含默认样式”,否则我们不会查看资源字典。存在一个错误:如果所有默认样式都嵌套在深度为三层或更深的合并字典中,则顶层字典不会被标记,因此搜索会跳过它。解决方法是在根字典中放置一个默认样式。

因此,在根字典中添加一个虚拟样式即可修复此问题。示例:

<Application x:Class="MyApp.App"  
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
    <Application.Resources> 
        <ResourceDictionary> 
            <ResourceDictionary.MergedDictionaries> 
                <ResourceDictionary 
                    Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" /> 
                </ResourceDictionary.MergedDictionaries> 
            <!-- Dummy Style, anything you won't use goes --> 
            <Style TargetType="{x:Type Rectangle}" /> 
        </ResourceDictionary> 
    </Application.Resources> 
</Application>   

这个 bug 只出现在较旧版本的 .NET 4 中:具体来说是 4.0.30319.1008。我在 XP 和一些未更新的 Windows 7 安装中看到过这种情况。 - Cat Zimmermann
Microsoft Connect的链接已经失效。 - Firo

14

请检查App.xaml.cs中的构造函数是否调用了InitializeComponent() - 这个方法会合并资源字典...


2
这对我非常有帮助。我一直在尝试弄清楚为什么我的页面(托管在框架中)没有获取我在App.xaml中定义的资源。原来是构造函数没有调用InitializeComponent。谢谢Chris!!! - NathanAW

5
您无需引用generic.xaml,因为它具有内置支持。但这意味着它提供了默认样式,您不需要显式设置。必须从明确引用的资源字典中获得显式设置的样式/模板。
(编辑以澄清)
一个例外是App.xaml,其中定义的资源可以在整个应用程序中访问,而无需引用任何特定的资源字典。该资源本身必须可通过名称访问。
原因为:
<Application.Resources>
    <ResourceDictionary
        Source="/CommonLibraryWpfThemes;component/Themes/Generic.xaml" />
</Application.Resources>

我认为原因是您没有将它包装在 MergedDictionary 中并将其添加到合并的字典中。直接添加到资源中仅适用于您本地声明的资源,例如样式等本身。
但是,正如我之前所说,您不必在任何地方合并 generic.xaml,也许您只需重构在样式外使用的画笔和其他资源,并仅在 app.xaml 中合并那些资源。
另请注意,样式不必在 generic.xaml 中具有“默认样式”行为 - 如果具有键等于元素类型的样式对其可访问(全局或在本地资源中),则它将使用该样式作为默认样式。 generic.xaml 只是一种方便。
请查看此答案
对于其他自定义画笔等,您需要显式引用这些资源。
您还应检查 WindowDictionary.xaml 的内容,此错误有一定的味道。

所以我认为你是在说直接在App.xaml中引用ResourceDictionaries是必要的,也是唯一的方法才能使它起作用。也就是说,我没法"预合并"所有的字典然后只从App.xaml中引用已经预合并的实体。虽然不太方便,但如果这就是它的工作方式,那也不是世界末日。到目前为止,我总是使用 {StaticResource MyResourceName} 显式地设置我的样式属性,所以我猜我需要重构很多东西来利用Generic.xaml允许的默认行为。我理解得对吗? - devuxer
当您使用app.xaml合并字典时,您使资源“全局化”,整个应用程序都可以访问(正如名称所示)。 app.xaml中的资源不需要通过字典名称引用,只需要通过特定的资源键引用即可。我可能在这方面表述不够清楚,我会澄清答案。我通常为应用程序的不同部分创建分组合并的字典,并根据需要在xaml中引用该字典。 - Kenan E. K.
感谢您澄清您的答案,但是我可能误解了您所说的“将其包装在MergedDictionary包装器中”的含义,或者那也不起作用。我在我的问题底部放置了一个代码示例的编辑。 - devuxer
没错,你做得对,虽然我不确定为什么它不起作用。也许这是与generic.xaml直接相关的某个bug。尝试将其他字典合并到同一位置并使用其中的一些资源,只是为了看看它是否能正常工作。 - Kenan E. K.
如果我将代码粘贴到另一个文件(MergedDictionary.xaml)中也不起作用 :( - devuxer

0
我在我的单元测试中遇到了这个错误,而Chris上面的答案给了我需要的线索。基本上在我的第一个被测试的方法中,我放置了以下代码:
        MyApplication.App app = new MyApplication.App();
        app.InitializeComponent();

突然间,它可以找到我的页面模板。注意:这意味着如果您也在单元测试App.cs,则必须检查是否已存在您的App实例。


-1

我的解决方案在这里, 点击“解决方法”。


2
因为链接没有链接文本,没有任何解释,外部内容-无法控制内容而被投票否决。 - trapicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接