I have an application that loads a series of resource dictionaries programmatically at startup.
I have derived AdvancedResourceDictionary
from ResourceDictionary
and I set its Source
property to a XAML file containing an AdvancedResourceDictionary
object, like so:
var ard = new AdvancedResourceDictionary();
ard.Source = new Uri("/Sandbox.Dicts;component/MainDict.xaml", UriKind.Relative);
Advanced ResourceDictionary is defined as thus:
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Threading;
using System;
namespace Sandbox.Dicts
{
public class AdvancedResourceDictionary : ResourceDictionary
{
protected ObservableCollection<ResourceDictionary> _staticDictionaries;
public ObservableCollection<ResourceDictionary> StaticDictionaries
{
get { return _staticDictionaries; }
set
{
if (value == _staticDictionaries) return;
if (_staticDictionaries != null)
_staticDictionaries.CollectionChanged -= StaticDictionariesCollectionChanged;
_staticDictionaries = value;
if (StaticDictionaries != null)
StaticDictionaries.CollectionChanged += StaticDictionariesCollectionChanged;
}
}
private void StaticDictionariesCollectionChanged(object sender_, NotifyCollectionChangedEventArgs e_)
{
if (e_.NewItems == null)
return;
foreach (ResourceDictionary rd in e_.NewItems)
{
if (rd.Source != null)
{
MessageBox.Show("Desired Behaviour with .NET 3.5");
}
else
{
MessageBox.Show("Undesired Behaviour with .NET 3.5");
}
}
}
public AdvancedResourceDictionary()
{
StaticDictionaries = new ObservableCollection<ResourceDictionary>();
}
}
}
MainDict.xaml contains a child resource dictionary itself. MainDict.xaml and its child dictionary (BaseDict.xaml) are below:
MainDict.xaml
<eos:AdvancedResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:eos="clr-namespace:Sandbox.Dicts">
<eos:AdvancedResourceDictionary.StaticDictionaries>
<ResourceDictionary Source="/Sandbox.Dicts;component/MyDicts/BaseDict.xaml" />
</eos:AdvancedResourceDictionary.StaticDictionaries>
<Style x:Key="test" TargetType="{x:Type Window}"/>
</eos:AdvancedResourceDictionary >
BaseDict.xaml
<eos:AdvancedResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:eos="clr-namespace:Sandbox.Dicts">
<SolidColorBrush x:Key="testbrush">#FF494949</SolidColorBrush>
</eos:AdvancedResourceDictionary>
Now, here's the problem. If my project targets .NET Framework 3.5, everything works fine. By the time StaticDictionariesCollectionChanged
event handler is called (which is basically the CollectionChanged
event for AdvancedResourceDictionary
's only property StaticDictionaries
which is populated in MainDict.xaml), the Source
property of its ResourceDictionary
element has been assigned.
However, if I change my project to target .NET Framework 4 instead, this is no longer the case. When StaticDictionariesCollectionChanged
is called, the Source
property of the ResourceDictionary
element is still null.
The only way around this seems to be by calling the body of the StaticDictionariesCollectionChanged
event handler on the dispatcher to give the resource dictionary time to load before the code is executed.
The reason behind using this AdvancedResourceDictionary
is my full application is to save memory by sharing instances of common resource dictionaries included in multiple parent resource dictionaries. I have omitted the code that does this optimisation, but this is basically the purpose. However, it may be that .NET 4 performs this optimisation already - if anyone can shed light on this, would be most grateful. Furthermore, the memory saving is not substantial, so I wonder if it's worth it anyway.
Also, the solution of using the dispatcher is not great either because the delegate doesn't actually get executed in time for those resource dictionaries to be required later on during startup. So, it's slow enough for Source
to get assigned before the code is executed, but not fast enough (even on highest priority) to be executed before it is needed.
I would like to know if there is a solution to this problem or if I should just changed all my uses of AdvancedResourceDictionary
to just simply use the standard ResourceDictionary
instead.
To demonstrate the issue, I have actually created three projects, one with .NET 3.5, one with .NET 4 not working, and one with .NET 4 with dispatcher workaround. They are in a zip file here: http://repository.x-treem.net/Code%20Snippets/ResourceDictionaryProblem.zip
Thanks, Jason