Search code examples
wpfxamlresourcedictionary

How to deal with Resource Dictionaries, so they don't create unnecessary objects


I moved some of my resources which are commonly used to a ResourceDictionary, example

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style x:Key="SaveButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="IsDefault" Value="True" />
    <Setter Property="IsCancel" Value="False" />
    <Setter Property="ToolTip" Value="Close and save any changes you have made." />
  </Style>
  <Style x:Key="CloseButtonStyle" BasedOn="{StaticResource XSaveCloseButtonStyle}" TargetType="{x:Type Button}">
    <Setter Property="IsDefault" Value="False" />
    <Setter Property="IsCancel" Value="True" />
    <Setter Property="ToolTip" Value="Close window." />
  </Style>
 </ResourceDictionary>

The reason for this is simple, I need the same styles in multiple places. But there is a problem I found, if somewhere I need to use CloseButtonStyle only, and I reference this resource dictionary. I think it creates both styles, I did a little test and on the code behind I looked by method FindResource to see whether I’ll get an instance of each style and I did. So my question is how do I deal with this? I don’t want to create 2 objects if I only need one, but I also don’t want to write this styles by copying and pasting it in many places.


Solution

  • You could do the following:

    1. Store styles in files in your working directory or resources (one file for one style)
    2. Create class (for example ResourcesHolder) that will provide you resources by its Key (like FindResource does)
    3. ResourcesHolder has FindResource(string resourceName) method that:
      • searches file in resources with the same name as resourceName
      • parses it with XamlReader and returns
    4. ResourcesHolder class contains Dictionary where parsed objects are stored. To, if any resource had already been parsed, you will return it from this dictionary:

      private Dictionary<string, object> storedObjects;
      
      public object FindResource(string resourceName)
      {
          if (storedObjects.ContainsKey(resourceName))
              return storedObjects[resourceName];
      
          else
              //search, parse and return new resource
      }
      

      So, you can load every resource individually without loading anything else

    EDIT:

    Before calling Initialize() method you could load all the resources that you will need to create your window/usercontrol. In this case, you could add method to ResourceHolder:

        public void Load(string resourceName)
        {
            if (Application.Current.Resources.Contains(resourceName))
                return;
            else
            {
                var obj = /*load your resource here*/;
                Application.Current.Resources.Add(resourceName, obj)
            }
        }
    

    And in your control/window:

        public partial class TestWindow : Window
        {
            public TestWindow()
            {
                ResourceHolder.Load("SaveButtonStyle");
                ResourceHolder.Load("CloseButtonStyle");
                InitializeComponent();
            }
        }
    

    In this case in your xaml you can use {StaticResource} extension

    If you want do it automatically in xaml like {StaticResource} or {DynamicResource}, you will have to write your own markupextension that would call Load or FindResource method of ResourceHolder itself