Notes on Software

Archive for the ‘Silverlight’ Category

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>
Advertisements

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

Differences between WPF and Silverlight – 1

Posted by K. M. on February 25, 2011

This is the first post in a series documenting the differences between WPF (v3.5+) and Silverlight (v4).

DataTemplates in an ItemsControl in bound mode (when ItemsSource is set):

An ItemsControl generates a container for each item in the ItemsSource. The container typically displays the item via a ContentPresenter (either directly or through a ControlTemplate for the container).

WPF:

The logic in the PrepareContainerForItemOverride method sets the ContentTemplate of the container to the ItemTemplate property if it is not null and the ContentTemplateSelector to the ItemTemplateSelector if it is not null. Additionally, the ItemContainerStyle gets applied to the container. This results in the following order of precedence for the data template

  1. ItemsControl.ItemTemplate
  2. ContentTemplate from ItemsControl.ItemContainerStyle
  3. ItemsControl.ItemTemplateSelector
  4. ContentTemplateSelector from ItemsControl.ItemContainerStyle

Silverlight:

There is no DataTemplateSelector. Also the logic in the PrepareContainerForItemOverride method sets the ContentTemplate of the container to the ItemTemplate property even if it is null. This results in the ItemsControl always using the ItemTemplate property for the data template. The ContentTemplate from ItemsControl.ItemContainerStyle is always ignored.

My comments:

The silverlight implementation is broken and fixing it will mean a breaking change. So I doubt if it will ever be fixed. It seems that the developers who implemented this either did not understand the concept of dependency property precedence or did not care to compare the implementation with the WPF one. Bad.

Posted in .NET, Silverlight, WPF | Tagged: , , , | Leave a Comment »