Conditional Template Selector for XAML Applications for Windows and Uno Platform

Ok this post took a bit of inspiration from the C# Markup support that’s available for the Uno Platform where it’s easy to build a template selector (for a ListView for example) using a set of Case statements. This got me thinking that there must be a way that we can use a similar technique in XAML. Most of the time in XAML when we want to use a template selector, we switch to C#, create a new class that inherits from DataTemplateSelector and would typically expose multiple properties for the different DataTemplates to switch between, and some logic in the SelectTemplateCore method. But what if we could build a generic conditional data template that would allow us to add any number of DataTemplates that would be used for a specific value of a property.

A simple implementation of the ConditionalDataTemplateSelector would be as follows.

[ContentProperty(Name = nameof(Templates))]
public class ConditionalDataTemplateSelector : DataTemplateSelector
{
    public string? ConditionalProperty { get; set; }
    public IList<ConditionalTemplate> Templates { get; } = new List<ConditionalTemplate>();

    protected override DataTemplate? SelectTemplateCore(object item, DependencyObject container)
    {
        if (item is MainViewModel.Item viewModelItem)
        {
            var value = viewModelItem.GetType().GetProperty(ConditionalProperty!)?.GetValue(viewModelItem);
            var template = Templates.FirstOrDefault(t => t.Value is not null && ( t.Value.Equals(value) || t.Value.Equals(value+string.Empty)))?.ValueTemplate;
            return template as DataTemplate;
        }

        return base.SelectTemplateCore(item, container);
    }
}
[ContentProperty(Name = nameof(ValueTemplate))]
public partial class ConditionalTemplate
{
    public DataTemplate? ValueTemplate { get; set; }

    public object? Value { get; set; }
};

Here we can see that the ConditionalDataTemplateSelector has two properties: ConditionalProperty which is the name of the property on the source item to look up the current value, and Templates which is a list of ConditionalTemplate. As we can see from the class definition the ConditionalTemplate has a ValueTemplate property which represents the DataTemplate and a Value property which is the value for which this DataTemplate would be selected.

Ok, this probably sounds all very theoretical, so let’s see this in action in a simple example. Firstly, some C# that shows the MainViewModel, which exposes an array of Item, which is made up of a Name and Status, where Status is an Enum with values None, Loading and Loaded.

public class MainViewModel
{
    public enum Status
    {
        None,
        Loading,
        Loaded
    }

    public class Item
    {
        public string? Name { get; set; }
        public Status Status { get; set; }
    }

    public Item[] Items { get; } = [
        new Item { Name = "Item 1", Status = Status.None },
        new Item { Name = "Item 2", Status = Status.Loading },
        new Item { Name = "Item 3", Status = Status.Loaded }
    ];
}

Now the XAML. Here we are defining three different DataTemplate that are appropriately keyed as NoneTemplate, LoadingTemplate and LoadedTemplate.

<Page x:Class="DataTemplateSelectorApp.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:DataTemplateSelectorApp"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Page.Resources>
    <DataTemplate x:Key="NoneTemplate">
      <TextBlock Text="None"/>
    </DataTemplate>
    <DataTemplate x:Key="LoadingTemplate">
      <TextBlock Text="Loading...."/>
    </DataTemplate>
    <DataTemplate x:Key="LoadedTemplate">
      <TextBlock Text="{Binding Name}"/>
    </DataTemplate>
    <local:ConditionalDataTemplateSelector
      x:Key="ConditionalSelector"
      ConditionalProperty="Status">
      <local:ConditionalTemplate
        ValueTemplate="{StaticResource NoneTemplate}"
        Value="None" />
      <local:ConditionalTemplate
        ValueTemplate="{StaticResource LoadingTemplate}"
        Value="Loading" />
      <local:ConditionalTemplate
        ValueTemplate="{StaticResource LoadedTemplate}"
        Value="Loaded" />
    </local:ConditionalDataTemplateSelector>
  </Page.Resources>

  <ListView ItemsSource="{Binding Items}"
            ItemTemplateSelector="{StaticResource ConditionalSelector}"/>
</Page>

The ConditionalDataTemplateSelector resource specifies the Status property to be inspected on each element and then for each ConditionalTemplate a corresponding Value is specified. As the ListView renders each item in the Items array, the ConditionalSelector will be invoked. The Status property on the Item will be inspected and the value compared to the Value of each ConditionalTemplate. If there’s a match, the corresponding DataTemplate will be returned.

Here’s a rather uninspiring picture of this sample app running, showing each of the three DataTemplate being returned.

As we’ve seen in this post it is possible to build a generic template selector that is able to pick between any number of templates based on some conditional value. The only downside with this implementation is that it does leverage reflection at runtime to retrieve the value on the bound object. I’m sure that it would be possible using source code generation to generate strongly typed code that would replace the reflection based implementation.

1 thought on “Conditional Template Selector for XAML Applications for Windows and Uno Platform”

Leave a comment