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.
source to share
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?
source to share
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.
source to share