【问题标题】:MergedDictionaries and Resource lookup合并字典和资源查找
【发布时间】:2011-07-14 12:44:27
【问题描述】:

我通常对资源字典和合并字典有疑问,尤其是在资源查找性能方面。经过一些性能测试后,我发现 ResourceDictionary.get_MergedDictionaries 是点击次数最多的调用(在 ANTS 分析器中检查)。我们有大约 300 个资源字典 xamls,其中很多都使用合并字典来“包含”其他样式。好吧,get_MergedDictionaries 依赖于我们应用程序的一部分,其中没有发生太多事情,大约有 1000 万次点击。所以我的猜测是我们对资源字典做的完全错误。所以我试图重构一切,我想尝试摆脱所有合并的字典。

现在回到实际问题。我试图摆脱合并的字典,但失败了。我的理解是,当您使用 StaticResource 时,查找需要在当前资源之前定义资源。我做了以下简短的例子:

一个主项目和一个自定义控件库。

自定义控件库包含 2 个 xamls。

<!-- Colors.xaml -->
<ResourceDictionary [stripped namespaces] >
    <SolidColorBrush x:Key="myColor" Color="Green"/>
</ResourceDictionary>

<!-- Templates.xaml -->
<ResourceDictionary [stripped namespaces]>
    <ControlTemplate x:Key="myTemplate" TargetType="Button">
        <Rectangle Fill="{StaticResource myColor}"/>
    </ControlTemplate>
</ResourceDictionary>

现在在主项目中,MainWindow.xaml 看起来像这样

<Window x:Class="ResourceTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/ResourceTestLib;component/Themes/Colors.xaml"/>
                <ResourceDictionary Source="/ResourceTestLib;component/Themes/Template.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <Button Template="{StaticResource myTemplate}"/>
    </Grid>
</Window>

这是预期的目标。但不幸的是,这会崩溃,因为找不到资源“myColor”。我当然知道如何修复它,在 Templates.xaml 中添加一个合并字典并引用 Colors.xaml 但我一直认为,我从来没有真正检查过,资源是根据逻辑树和元素的资源来查找的。我的理解是;按钮已创建;尝试查找模板..找到;尝试查找颜色,在自己的资源上没有找到,上去使用windows资源。

看来我错了。 所以我希望有人可以为我阐明这一点。我们大量使用 WPF,尽管如此,我们还是用它完成了很多工作,但是由于一开始的一些错误的学习行为,我们的性能非常糟糕,仅仅是因为资源查找。 任何帮助将不胜感激

提前致谢 最好的祝福 妮可

【问题讨论】:

  • 在 MyColor 中使用 DynamicResource 而不是 StaticResource 有什么问题?最后我检查了一下,性能差异不足以证明不使用动态资源。
  • 嗯,我主要关心的是性能,我读到的每个性能提示都提到不要使用 DynamicResource,即使 MSDN 也这么说。我的示例源代码也只包含我们问题的最小示例。所以我们讨论了大约 3000-4000 次 StaticResource 调用。
  • 你为什么有这么多?通常资源只加载到一个应用程序中一次,因此您应该在整个应用程序中只有一个对Colors.xamlTemplates.xaml 的引用。通常我将通用资源(颜色、模板、样式等)加载到 Application.Resources 中,而不是将特定资源(如 DataTemplates)加载到使用它们的 UserControl 中。
  • 附带说明,我发现 Blend 几乎完全是用 DynamicResources 编写的,它生成的代码总是使用 DynamicResource。如果静态资源和动态资源之间存在显着的性能差异,我认为他们不会这样做。
  • @Rachel 就像我在第一篇文章中写的一样。我一开始就学错了或者理解错了。我们有很多自定义控件,每个自定义控件都有自己的 xaml 及其样式。如果它是一个项目控件,我们还有一个用于容器的 xaml。我们还进一步拆分了 xaml,因此设计者有一个 xaml,他只能在其中修改与设计器相关的内容,而控件的主要 xaml 仅引用设计器的内容。我们这样做是为了让设计人员不必处理绑定和其他与代码相关的事情。看起来这种整洁的结构使情况变得更糟。

标签: wpf performance resourcedictionary staticresource mergeddictionaries


【解决方案1】:

好吧,我不喜欢回答我自己的问题,但我想很多人可能会偶然发现这个问题,我想给他们提供我们当前的解决方案作为一个可供考虑的选项。

就像我之前所说的,我们有很多 XAML,大约 300 个用于各种不同类型的东西,例如共享资源(画笔、颜色),但也有许多 XAML 包含不同的数据模板、控件样式以及自定义控件。一开始,这种拥有大量 XAML 的方法对我们来说是合理的,因为我们对我们的类做同样的事情,并使它们保持小而有条理。 不幸的是,WPF 不喜欢这样。您拥有的 ResourceDictionaries 越多,通过 MergedDictionaries 合并它们的次数越多,您的性能就会越差。 我能给你的最好建议是,尽可能少使用 ResourceDictionary XAML

我们咬紧牙关,将其中的许多合并到一个巨大的 XAML 中,事实上,我们现在使用预编译器来做到这一点,保持两全其美。我们可以根据需要使用任意数量的 XAML,只需遵循一些约束,并将它们合并到一个巨大的 XAML 中的编译中。我们获得的性能提升非常显着。在我的问题中,我写了“getMergedDictionaries 上的 1100 万次点击”......只是“预编译”了我们的一个程序集,我们的点击量下降到了 200 万次,而且整个应用程序的性能在任何时候都好很多。

所以最后。 XAML 资源不应被视为经过编译的源代码,而应将其理解为实际存在的资源,在声明时它会占用空间和性能。

好吧,我们必须以艰难的方式学习这一点。我希望阅读本文的每个人都可以通过从我们的错误中吸取教训来改进他们的项目。

【讨论】:

  • 我想你不会愿意分享你的预编译细节,或者至少给我指出正确的方向,对吗?
  • 很遗憾不是,因为它不是我的代码,而是我工作的公司。但这并不难做到。只需解析您的 xamls 以获取合并的字典,通过该信息将每个文件关联起来,以了解您需要以什么顺序合并它们。然后创建一个新的 xaml 并按该顺序将每个 xaml 写入其中。我不得不重命名所有原始 xaml,所以最后只有一个真正的 *.xaml 存在。我调用了所有原始 xamls,*.tamls。当然,您必须处理具有相同名称以及名称空间和别名的资源。但这也很容易解决。
  • 它是作为预构建步骤运行的控制台应用程序,还是 VS 不可或缺的东西?
  • 不,我只是构建了一个非常简单的控制台应用程序,并将其设置为预构建步骤,就像你说的那样。它有多种模式,因为我必须第一次准备项目,通过重命名所有 xamls,所以它们不会被 vs 编译,我只是将它们转换为简单的资源。之后每次编译,获取所有所谓的 tamls,解析它们,解析命名空间并将它们写入一个巨大的 xaml,然后在编译时由 VS 使用。
  • 我遇到了类似的问题并创建了这样一个预构建事件程序。您可以在GitHub 上找到它。它包含一个示例应用程序,您可以看到所有 XAML 文件被合并到一个长 XAML 文件中。我在blog post 中写了完整的解释。享受:)
【解决方案2】:

我倾向于在应用程序中只使用一个 ResourceDictionary 以避免任何性能问题。

为了使 XAML 易于管理,我使用 XAML 区域 Visual Studio 插件并将每个类别的资源包装在一个区域中。

  • 画笔
  • 文本样式
  • 等等……

对于这种情况,插件绝对是救命稻草。 http://visualstudiogallery.msdn.microsoft.com/3c534623-bb05-417f-afc0-c9e26bf0e177

【讨论】:

  • 我已经在使用它了。不过谢谢。组织不是问题。 VS 非常慢(关于接受按键,而不仅仅是显示智能感知窗口)。我已经尝试禁用 Resharper,这似乎在一定程度上提高了性能,但我不知道我是否可以仅为那个文件(或 XAML 文件)禁用它,而且我不希望仅仅为了编辑而丢失那些强大的工具文本文件。
【解决方案3】:

使用 SharedResourceDictionary 而不是 ResourceDictionary 为我完全解决了 MergedDictionaries 性能问题: http://www.wpftutorial.net/MergedDictionaryPerformance.html

【讨论】:

  • 我们已经使用了它们,虽然我们没有获得那么快的速度,但我们节省了很多内存。但是Sharedresource字典有内存泄漏的问题,我也发了here
【解决方案4】:

资源查找过程是否有任何启示?为什么找不到“myColor”?

顺便说一句,我找到了一种让它工作的方法——但一种奇怪且不稳定的方法。如果Application.xaml有这个代码,应该可以找到颜色:

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/ResourceTestLib;component/Themes/Colors.xaml"/>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/ResourceTestLib;component/Themes/Template.xaml"/> 
        </ResourceDictionary.MergedDictionaries> 
    </ResourceDictionary>
</ResourceDictionary.MergedDictionaries>

另一方面,如果您将此代码包含到另一个 XAML 中,然后您将其包含到 Application.xaml 中 - 它不起作用,即使资源结构相同(使用 Snoop 验证)。

【讨论】:

  • 据我所知。该资源将在其当前 xaml、其当前 MergedDictionaries、Generic.xaml(在某些情况下)以及 app.xaml 及其 MergedDictionaries 中查找 MyColor。这样它就可以正常工作。我们现在所做的是,删除所有合并的字典,将很多 xml 的 xaml 合并到一个 xaml 中,并将它们全部合并到 app.xaml 中。所以 app.xaml 或多或少是唯一允许合并字典的文件。您必须特别注意 app.xaml 中“包含”的顺序,但请记住这一点,它可以顺利运行。
猜你喜欢
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多