Combining dictionaries into multiple custom controls - does this affect memory?

I have a UI project containing (so far) 18 XAML resources that are bundled in App.xaml like this: -

<Application ...>
   <Application.Resources>
       <ResourceDictionary>
           <ResourceDictionary.MergedDictionaries>
               <ResourceDictionary Source="pack://application:,,,/foo.Client.Common;component/UIStyles/Colours.xaml" />
               <ResourceDictionary Source="pack://application:,,,/foo.Client.Common;component/UIStyles/Buttons.xaml" />
               <ResourceDictionary Source="pack://application:,,,/foo.Client.Common;component/UIStyles/Menus.xaml" />
               // etc..
           </ResourceDictionary.MergedDictionaries>
       </ResourceDictionary>
   </Application.Resources>
</Application>

      

When I edit any of the XAML views in this project, I get intellisense (Resharper) in the names of the styles contained in those resources.

The application now also implements a "plug-in" architecture, and there are several class library projects containing views and view models; they are loaded dynamically at runtime. Although the app is working fine, I am not getting the intellisense style name when I edit the XAML views in these projects. I can get intellisense to work by concatenating the required styles as XAML like: -

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/foo.Client.Common;component/UIStyles/Buttons.xaml" />
            //etc..
        </ResourceDictionary.MergedDictionaries>

        // User control-specific styles go here.
    </ResourceDictionary>
</UserControl.Resources>

      

By doing this in every custom control, am I "duplicating" every resource and therefore inflating the application's memory requirements? If so, is there a way to get around this? What if I created a resource file in a class library, combined 15 "core" resources into it, and then combined that one resource into my views? Will it still lead to the same problem?

Otherwise, I assume that I need to keep the "XAML merge" in each view and only temporarily restore it when I need to work on the view.

+3


source to share


2 answers


Good question. I have the same problem.

There are two problems here:

1) Annoying duplication in Xaml code - I'm not talking about duplicating real resources and you have to write <ResourceDictionary>

everywhere.

2) Actual duplication of resources is a waste of resources, plus: you will lose the benefit of replacing styles at runtime if they are not co-located.

HOw can you solve these problems?

By creating a new xaml file, calling it SharedResourceDictionary.xaml

which includes all resources. This will be included App.xaml

.

This will allow you to pull ResourceDictionary

inside other xaml components, in a nutshell, just include SharedResourceDictionary.xaml

.



The second trick is to make sure the resources are only created once:

[assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", 
"WPFTutorial.Utils")]

/// <summary>
/// The shared resource dictionary is a specialized resource dictionary
/// that loads it content only once. If a second instance with the same source
/// is created, it only merges the resources from the cache.
/// </summary>
public class SharedResourceDictionary : ResourceDictionary
{
    /// <summary>
    /// Internal cache of loaded dictionaries 
    /// </summary>
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
        new Dictionary<Uri, ResourceDictionary>();

    /// <summary>
    /// Local member of the source uri
    /// </summary>
    private Uri _sourceUri;

    /// <summary>
    /// Gets or sets the uniform resource identifier (URI) to load resources from.
    /// </summary>
    public new Uri Source
    {
        get { return _sourceUri; }
        set
        {
            _sourceUri = value;

            if (!_sharedDictionaries.ContainsKey(value))
            {
                // If the dictionary is not yet loaded, load it by setting
                // the source of the base class
                base.Source = value;

                // add it to the cache
                _sharedDictionaries.Add(value, this);
            }
            else
            {
                // If the dictionary is already loaded, get it from the cache
                MergedDictionaries.Add(_sharedDictionaries[value]);
            }
        }
    }
}

      

If that doesn't work, add some comments below: http://www.wpftutorial.net/MergedDictionaryPerformance.html

It is also possible to explore the land of the xaml code ifdef:

Does XAML have a conditional compiler directive for debug mode?

+2


source


I'll reply to Chris Elmaa's answer as an answer, but see my comments below. If anyone is interested, here is a modified version of SharedResourceDictionaryClass that includes a couple of tweaks to improve design-time support and make it thread-safe, based on the comments left on the wpftutorial site linked to Chris: -

public class SharedResourceDictionary : ResourceDictionary
{
    private static readonly Dictionary<Uri, ResourceDictionary> SharedDictionaries =
        new Dictionary<Uri, ResourceDictionary>();
    private Uri _sourceUri;

    /// <summary>
    /// Gets or sets the uniform resource identifier (URI) to load resources from.
    /// </summary>
    public new Uri Source
    {
        get
        {
            return IsInDesignMode ? base.Source : _sourceUri;
        }
        set
        {
            if (IsInDesignMode)
            {
                try
                {
                    base.Source = value;
                }
                catch
                {
                }
                return;
            } 

            _sourceUri = new Uri(value.OriginalString);

            if (!SharedDictionaries.ContainsKey(value))
            {
                base.Source = value;
                lock (((ICollection)SharedDictionaries).SyncRoot)
                {
                    SharedDictionaries.Add(value, this);
                }
            }
            else
            {
                lock (((ICollection)MergedDictionaries).SyncRoot)
                {
                    MergedDictionaries.Add(SharedDictionaries[value]);
                }
            }
        }
    }

    /// <summary>
    /// Gets a value indicating whether [is in design mode].
    /// </summary>
    private static bool IsInDesignMode
    {
        get
        {
            return (bool)DependencyPropertyDescriptor.FromProperty(
                DesignerProperties.IsInDesignModeProperty,
                typeof(DependencyObject)).Metadata.DefaultValue;
        }
    }
}

      

(Not sure why SO isn't coloring this code!)

Then replace the ResourceDictionary with a SharedResourceDictionary (along with the corresponding XML namespace of course) like: -



<ResourceDictionary.MergedDictionaries>
    <foo:SharedResourceDictionary  Source="pack://application:,,,/foo.Client.Common;component/UIStyles/Colours.xaml" />

      

Intellisense works with this locally, i.e. you get a list of valid styles when you type something like Style="{StaticResource

, however the style names are underlined with orange squiggles and the message "Style" xyz "cannot be resolved." (I'm not sure if this is Resharper - I've been using it for so long, I don't know what R # is and what the standard editor is!)

In terms of memory, I noticed a 1.5% drop in memory usage when I did this so nothing ruined the ground. For now, I think I will stick with what I got, because there is nothing wrong with the memory as I expected.

0


source







All Articles