Notes on Software

Posts Tagged ‘ResourceDictionary’

Shared ResourceDictionary for Silverlight

Posted by K. M. on April 5, 2011

The problem:

Both WPF and Silverlight support merged ResourceDictionary usage via the MergedDictionaries property.  Typically this is done with markup similar to the following in App.xaml

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Shared.xaml"/>
                <ResourceDictionary Source="Resources/Views1.xaml"/>
                <ResourceDictionary Source="Resources/Views2.xaml"/>
                <ResourceDictionary Source="Resources/Views3.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

In WPF any resources defined within Shared.xaml are accessible by a StaticResource reference from any of the other dictionaries because of the order in which they are merged into App.xaml. In Silverlight, all the dictionaries are loaded independently and StaticResource references across dictionaries do not work. To get the references to work, Shared.xaml has to be merged into each of the other dictionaries. However, this means that the contents of Shared.xaml will be loaded and duplicated at each place where it is merged in. This is clearly bad. While searching for a solution I came across this post by Christian Mosers. Mosers defines a SharedResourceDictionary class deriving from ResourceDictionary and redefines the Source property where he uses some caching logic to avoid reloading the dictionary. Unfortunately this solution does not work in Silverlight. For some reason, Silverlight does not allow the same dictionary instance to be merged into multiple dictionaries. Additionally, the Visual Studio designer throws an exception in the setter of the Source property. Apparantly Visual Studio does not like derived classes.

The solution:

The following class solves both the problems detailed above

public class SharedResourceDictionary : ResourceDictionary
{
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
       new Dictionary<Uri, ResourceDictionary>();

    private Uri _sourceUri;
    public new Uri Source
    {
        get { return _sourceUri; }
        set
        {
            _sourceUri = value;
            if (!_sharedDictionaries.ContainsKey(value))
            {
#if SILVERLIGHT
                var dict = new ResourceDictionary();
                Application.LoadComponent(dict, value);
#else
                var dict = (ResourceDictionary)Application.LoadComponent(value);
#endif
                _sharedDictionaries.Add(value, dict);
            }
            CopyInto(this, _sharedDictionaries[value]);
        }
    }

    private static void CopyInto(ResourceDictionary copy, ResourceDictionary original)
    {
        foreach (var dictionary in original.MergedDictionaries)
        {
            var mergedCopy = new ResourceDictionary();
            CopyInto(mergedCopy, dictionary);
            copy.MergedDictionaries.Add(mergedCopy);
        }
        foreach (DictionaryEntry pair in original)
        {
            copy.Add(pair.Key, pair.Value);
        }
    }
}

    This bypasses the ResourceDictionary.Source property entirely by using Application.LoadComponent directly, thus keeping the Visual Studio designer happy. It copies the cached ResourceDictionary to keep Silverlight happy. This is slightly less efficient than merging in the same cached instance everywhere but it is certainly much better than reloading every resource multiple times. The only catch is that the Source property must be set in a way that Application.LoadComponent can resolve it correctly. I use this with absolute URIs.

Sample usage:

Markup similar to the following can be used at the beginning of each resource dictionary that needs to reference resources in Shared.xaml

    <ResourceDictionary.MergedDictionaries>
        <ui:SharedResourceDictionary Source="/my_assembly_name;component/Resources/Shared.xaml"/>
    </ResourceDictionary.MergedDictionaries>

Posted in Silverlight, WPF | Tagged: , , | 15 Comments »