5

I have found this post about improving performance of ResourceDictionaries on the web, but the problem is that it's pretty old (2011). I was thinking about implementing something like this, but I'm wondering if Microsoft has not fixed this in the last releases of .NET Framework. I have few questions regarding this topic, which, I hope someone here can give an answer to:

  1. Is .NET Framework 4.6.1 still managing ResourceDictionaries by making multiple instances of them, one for every time they are assigned to a control?
  2. Is it even an issue when I have for example style "CustomButtonStyle" in my ResourceDictionary called "ButtonStyles" in assembly called "StylesAssembly", which is then referenced in App.xaml of an application, that has 20 Buttons with CustomButtonStyle assigned to them?
  3. Do I understand it correctly, that in the case above, there will be 20 instances of "ButtonStyles" ResourceDictionary?
  • I guess it's very easy to test, but I have problem to understand the exact scenario. Do you mean another assembly with resource dictionary or what? – Sinatr Oct 18 '17 at 12:40
  • @mm8 I have edited point 1 to make it easier to understand, thanks for pointing out my mistake – Kacper Stachowski Oct 18 '17 at 12:41
  • @Sinatr the problem is that I have no idea how to test it, because `ResourceDictionary` have no code behind, so I can't put a breakpoint there. The scenario is: `StyleAssembly` is a `ClassLibrary`, which have `ResourceDictionary` called `ButtonStyles`, which have `CustomButtonStyle` in it. My app references `StyleAssembly` and merges it in App.xaml and my MainWindow has 20 `Buttons` using style `CustomButtonStyle`. Will it make my app create 20 instances of `ButtonStyles`? – Kacper Stachowski Oct 18 '17 at 12:44
  • 1
    I've tested such scenario with @mm8 method, there is no problem with it. Only one instance is created. One obvious case when multiple loads will occurs is directly writing `` in e.g. `UserControl.Resources`. This would indeed create a new instance of resource dictionary for each instance of such user control. Instead you have to define it once [per application](https://stackoverflow.com/a/2528827/1997232). – Sinatr Oct 18 '17 at 13:37

3 Answers3

4

Thanks @mm8 for posting your answer. It's 100% correct, I just want to post my own answer, because I found out something interesting, that can be useful for someone else.

The answer is: ResourceDictionary instance will be created just once if referenced in the application (no matter many controls uses its styles), BUT it will be instantiated again for every time it is referenced in another ResourceDictionary that is also used in the application.


So to give you example of this case let's say we have the following structure:

- StylesAssembly.dll
  - ButtonResourceDictionary.xaml
  - CustomButtonResourceDictionary.xaml

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

ButtonResourceDictionary.xaml has the following code:

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

CustomButtonResourceDictionary.xaml has the following code, which uses 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 has a reference to StylesAssembly.dll and there is a following code in the App.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>

Now if our MainWindow.xaml has something like this in it, the ButtonResourceDictionary.xaml will have only one instance:

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

but if our MainWindow.xaml has something like this in it, the CustomButtonResourceDictionary.xaml will have one instance, but the ButtonResourceDictionary.xaml will have two instances:

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

It happens because first two Buttons use style DefaultButtonStyle from ButtonResourceDictionary.xaml, but another three Buttons use style CustomButtonStyle which comes from CustomButtonResourceDictionary.xaml, which merges ButtonResourceDictionary.xaml in its code.

  • This is the expected behaviour. How would the merged dictonary be resolved without creating an instance of it? – mm8 Oct 18 '17 at 14:19
  • And I don't argue with it. It's just hard to find (from what I have experienced today) so I decided to get everything in one place and post it here. Maybe it will help someone in the future. – Kacper Stachowski Oct 18 '17 at 14:45
2

Do I understand it correctly, that in the case above, there will be 20 instances of "ButtonStyles" ResourceDictionary?

No. Only one.

Is it even an issue when I have for example style "CustomButtonStyle" in my ResourceDictionary called "ButtonStyles" in assembly called "StylesAssembly", which is then referenced in App.xaml of an application, that has 20 Buttons with CustomButtonStyle assigned to them?

If you merge the ResourceDictionary into your App.xaml and create 20 Button elements across the views in your application, there will still be only one instance of the ResourceDictionary class created.

You can confirm this yourself by adding a code-behind class to the ResourceDictionary:

Is it possible to set code behind a resource dictionary in WPF for event handling?

...and put a breakpoint in the constructor.

There will also be only one instance of the Style defined in the ResourceDictionary created.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks, I didn't know I can add a code behind class to a `ResourceDictionary`. I want to check it myself and if it works, I'll mark it as an answer. – Kacper Stachowski Oct 18 '17 at 12:50
  • @mm8, and what about statement *"Each time a control references a ResourceDictionary XAML creates a new instance of it"* from [here](https://wpftutorial.net/MergedDictionaryPerformance.html)? I am not saying you are wrong, but I suspect they used another scenario than you have tested. – Sinatr Oct 18 '17 at 12:51
  • The problem is that this post is from 2011. That is why I asked this question. It can be out of date already – Kacper Stachowski Oct 18 '17 at 12:52
  • @mm8 Thanks, it works as you said. I'll allow myself to add another answer to my question too, because I found out an interesting detail that can be helpful for someone else. – Kacper Stachowski Oct 18 '17 at 13:10
1

I have recently been working on an alternative solution with a friend and I wanted to share it. The goal is to be able to use ResourceDictionaries anywhere and merge them in whatever way necessary, but still have them instantiated only once and without breaking the VS designer and Blend etc.

Instructions:
1. Use merged ResourceDictionaries in xaml as you see fit.
2. Reference nuget packages Sundew.Xaml.Optimizations and Sundew.Xaml.Optimizer
3. Add a sxo-settings.json in the root of your project and enable the ResourceDictionaryCachingOptimizer
4. Build
The build will use a caching/shared ResourceDictionary, but designer will also work because they only see the normal ResourceDictionary.

For more details see: https://github.com/hugener/Sundew.Xaml.Optimizations
And a sample: https://github.com/hugener/Sundew.Xaml.Optimizer.Sample

Hugge
  • 21
  • 2