views:

12

answers:

0

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