TypeTemplateSelector for WinRT

In Halfwit RT I have a series of ViewModels that represent Twitter timelines, all deriving from a common abstract base class called (unsurprisingly) TimelineViewModel.

To represent instances of these timelines in XAML, I have a DataTemplate for each class. In WPF I would've made use of implicit DataTemplates and simply declared them like this:

<DataTemplate DataType="{x:Type vm:MentionsTimelineViewModel}">

However, WinRT doesn't have implicit DataTemplates, so I need a way to automatically choose the right DataTemplate for the ViewModel I'm viewing at the time.

In the current version of Halfwit RT I'm making use of the funky code in this post by Bart Roozendaal. However, I reckon I've improved on it.

Bart's code simply uses App.Current.Resources.TryGetValue to check for a DataTemplate with the same name as the type you're looking for. My new version goes a few steps further. Firstly, it caches the results (per instance of the template selector) so you don't have to look them up more than once. Secondly, it recursively searches for DataTemplates representing any implemented interfaces or base classes of the type. That means I can derive a "top level" DataTemplate like this:

<DataTemplate x:Key="Halfwit.ViewModels.Timelines.TimelineViewModel" />

... and all the classes that derive from TimelineViewModel will automatically pick that up unless I give them their own specific DataTemplate.

Here's the code. Enjoy!

public class TypeTemplateSelector : DataTemplateSelector
{
    public TypeTemplateSelector()
    {
        _cache = new Dictionary<String, DataTemplate>();
    }

    Dictionary<String, DataTemplate> _cache;

    DataTemplate GetTemplateForType(Type t)
    {
        if (t == null) return null;

        var key = t.ToString();

        DataTemplate result = null;
        if (_cache.TryGetValue(t.ToString(), out result)) return result;

        object resource;
        if (App.Current.Resources.TryGetValue(key, out resource))
        {
            result = resource as DataTemplate;
        }
        else
        {
            foreach (var i in t.GetTypeInfo().ImplementedInterfaces)
            {
                result = GetTemplateForType(i);
                if (result != null) break;
            }
            result = GetTemplateForType(t.GetTypeInfo().BaseType);
        }

        _cache.Add(key, result);
        return result;
    }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (null == item) return null;

        return GetTemplateForType(item.GetType());
    }
}
winrt halfwit c# .net xaml
Posted by: Matt Hamilton
Last revised: 28 Jul, 2017 02:47 PM History