同一 ResourceDictionary 的多个实例

5

我发现了这篇文章,它与Web上的ResourceDictionaries性能有关,但问题在于它相当古老(2011年)。我正在考虑实现类似的东西,但我想知道微软是否在最新版的.NET Framework中修复了这个问题。我对这个主题有一些问题,希望这里的某个人能给出答案:

  1. .NET Framework 4.6.1是否仍通过创建多个实例来管理ResourceDictionaries,每次将其分配给控件时都会创建一个实例?
  2. 例如,我的ResourceDictionary中有样式"CustomButtonStyle",称为"ButtonStyles"的程序集中,然后在应用程序的App.xaml中引用,其中有20个Buttons分配了CustomButtonStyle,这是否是一个问题?
  3. 我是否正确理解,以上情况将有20个实例的"ButtonStyles"ResourceDictionary

我猜测试很容易,但我不太理解确切的情境。你是指另一个带有资源字典的程序集吗? - Sinatr
@mm8,我已经编辑了第一点,使其更易于理解。感谢您指出我的错误。 - Kacper Stachowski
@Sinatr,问题是我不知道如何测试它,因为ResourceDictionary没有代码后端,所以我无法在那里设置断点。情况是这样的:StyleAssembly是一个ClassLibrary,其中包含名为ButtonStylesResourceDictionary,里面有CustomButtonStyle。我的应用程序引用了StyleAssembly并将其合并到了App.xaml中,我的MainWindow有20个使用CustomButtonStyle样式的按钮。这会导致我的应用程序创建20个ButtonStyles实例吗? - Kacper Stachowski
1
我已经使用@mm8的方法测试了这种情况,没有问题。只创建了一个实例。当多个加载发生时,一个明显的情况是直接在例如UserControl.Resources中编写<ResourceDictionary Source="pack://application:,,,/ClassLibrary;component/ResourceDictionary.xaml" />。这确实会为每个此类用户控件的实例创建一个新的资源字典实例。相反,您必须每个应用程序定义它一次。 - Sinatr
3个回答

4
感谢 @mm8 发布的答案。它是100%正确的,但我想发布自己的答案,因为我发现了一些有趣的东西,可以对他人有用。
答案是:如果应用程序引用了 ResourceDictionary 实例(无论有多少控件使用其样式),则只会创建一次,但是每次在另一个也在应用程序中使用的 ResourceDictionary 中引用它时,都会重新实例化。
因此,为了给您举例说明这种情况,假设我们有以下结构:
- StylesAssembly.dll
  - ButtonResourceDictionary.xaml
  - CustomButtonResourceDictionary.xaml

- Application.exe
  - App.xaml
  - MainWindow.xaml

ButtonResourceDictionary.xaml包含以下代码:

<Style x:Key="DefaultButtonStyle" TargetType="{x:Type Button}">
    <!-- Some setters -->
</Style>

CustomButtonResourceDictionary.xaml有以下代码,其中使用了ButtonResourceDictionary.xaml

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="ButtonResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource DefaultButtonStyle}">
    <!-- Some setters -->
</Style>

Application.exe 引用了 StylesAssembly.dllApp.xaml 中有以下代码:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/StylesAssembly;component/ButtonResourceDictionary.xaml" />
            <ResourceDictionary Source="pack://application:,,,/StylesAssembly;component/CustomButtonResourceDictionary.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

现在,如果我们的 MainWindow.xaml 中有以下内容,那么 ButtonResourceDictionary.xaml 将只有一个实例
<StackPanel>
    <Button Style="{StaticResource DefaultButtonStyle}" />
    <Button Style="{StaticResource DefaultButtonStyle}" />
    <Button Style="{StaticResource DefaultButtonStyle}" />
    <Button Style="{StaticResource DefaultButtonStyle}" />
    <Button Style="{StaticResource DefaultButtonStyle}" />
</StackPanel>

但是如果我们的MainWindow.xaml中有类似这样的内容,CustomButtonResourceDictionary.xaml将会有一个实例,但是ButtonResourceDictionary.xaml将会有两个实例

<StackPanel>
    <Button Style="{StaticResource DefaultButtonStyle}" />
    <Button Style="{StaticResource DefaultButtonStyle}" />
    <Button Style="{StaticResource CustomButtonStyle}" />
    <Button Style="{StaticResource CustomButtonStyle}" />
    <Button Style="{StaticResource CustomButtonStyle}" />
</StackPanel>

这是因为前两个使用了ButtonResourceDictionary.xaml中的DefaultButtonStyle样式,而另外三个使用了来自CustomButtonResourceDictionary.xamlCustomButtonStyle样式,后者在其代码中合并了ButtonResourceDictionary.xaml


这是预期的行为。如果不创建一个实例,那么如何解决合并字典呢? - mm8
我不会对此进行争论。只是很难找到(根据我今天的经验),所以我决定把所有东西都放在一个地方并在这里发布。也许将来会有人受益于此。 - Kacper Stachowski

2
我理解得对吗?在上述情况下,“ButtonStyles” ResourceDictionary将会有20个实例吗?
不是的,只有一个。
当我在名为“StylesAssembly”的程序集中的名为“ButtonStyles”的ResourceDictionary中拥有样式“CustomButtonStyle”,然后将其引用到应用程序的App.xaml中,并且有20个按钮分配了CustomButtonStyle,这是否是一个问题?
如果您将ResourceDictionary合并到App.xaml中,并在应用程序的视图中创建20个Button元素,则仍将仅创建ResourceDictionary类的一个实例。
您可以通过向ResourceDictionary添加代码后端类来自行确认:
https://dev59.com/93VD5IYBdhLWcg3wGHeu
并在构造函数中设置断点。
ResourceDictionary中定义的Style也将仅创建一个实例。

谢谢,我不知道我可以给ResourceDictionary添加一个代码后台类。我想自己验证一下,如果有效的话,我会将其标记为答案。 - Kacper Stachowski
@mm8,关于语句“每当控件引用一个ResourceDictionary,XAML就会创建一个新的实例”(来自这里),你怎么看?我不是说你错了,但我怀疑他们使用了另一种情况,而不是你测试过的。 - Sinatr
问题是这篇帖子是2011年的。这就是为什么我问这个问题。它可能已经过时了。 - Kacper Stachowski
@mm8 谢谢,按照你说的方法可以运行。我想再添加一个答案到我的问题中,因为我发现了一个有用的细节,可能对其他人也有帮助。 - Kacper Stachowski

1
我最近和一个朋友一起致力于开发一种备选方案,并想分享一下。目标是能够在任何地方使用ResourceDictionaries并以任何所需的方式合并它们,但仍然只实例化一次且不会破坏VS设计师、Blend等工具。
说明:
1. 根据需要在xaml中使用合并的ResourceDictionaries。
2. 引用nuget包Sundew.Xaml.OptimizationsSundew.Xaml.Optimizer
3. 在项目根目录下添加sxo-settings.json文件并启用ResourceDictionaryCachingOptimizer。
4. 构建。
构建将使用缓存/共享的ResourceDictionary,但设计师也能正常工作,因为他们只看到正常的ResourceDictionary。
更多细节请见:https://github.com/hugener/Sundew.Xaml.Optimizations
以及样例:https://github.com/hugener/Sundew.Xaml.Optimizer.Sample


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