Creating XAML lists that contain different types of items

Every now and then, I receive questions about how to display different types of items in a ListView, FlipView, Xamarin.Forms CarouselView, etc. For example, imagine a list of files the user can choose from. The list will mainly contain file names, but for image files we’d like to display the actual image file itself (or some kind of pre-rendered thumbnail image) instead of the file name.

As an easy solution we could just define a label and an image inside each list item, and hide one of them depending on whether there is an image to show or not. This will work perfectly fine for small amounts of list items, but for longer lists defining the double amount of visual controls than is actually needed (and will even be displayed) seems unnecessary and might leed to performance drawbacks. I don’t say this is a bad solution per se, as it is useful in some scenarios (e.g., it allows animating the fade in / fade out of the different types of controls within each list item), but I’d like to point you to the use of DataTemplateSelectors as an alternative solution (by the way, this approach works similarly in all XAML dialects, meaning you can use it even in WPF and Xamarin.Forms, but for this example let’s focus on a UWP Windows App):

We all know that for binding an arbitrary number of items to a UWP ListView (or any other ItemsControl-like control), we can declare a DataTemplate that defines the visual appearance of each list item. This way, the actual item objects don’t need to contain any display-related information (we can actually use simple POCOs):

<ListView ItemsSource="{Binding Files}">
	<ListView.ItemTemplate>
		<DataTemplate>
			<TextBlock Text="{Binding FileName}"/>
		</DataTemplate>
	</ListView.ItemTemplate>
</ListView>

One thing that is quite obvious but anybody hardly thinks about is that the DataTemplate can even be specified outside the ListView (e.g., within the page’s or the App’s global resources), and referenced as static resource:

<Page.Resource>
	<DataTemplate x:Key="FileListItemTemplate">
		<TextBlock Text="{Binding FileName}"/>
	</DataTemplate>
</Page.Resource>

<ListView ItemsSource="{Binding Files}" ItemTemplate="{StaticResource FileListItemTemplate}" />

This didn’t change anything in the way the list is displayed (which is why hardly anyone does it in practice for such a simple list), but it is the first step towards our goal of declaring multiple different DataTemplates to be used in the same list!

Let’s analyze this statement in detail: On the one hand, obviously nothing keeps us from defining several data templates within the page’s resources block – so if image files shall be displayed differently from all other files, we could rename our first DataTemplate and define a second one containing an image instead of a textual label:

<Page.Resource>
	<DataTemplate x:Key="GeneralFileItemTemplate">
		<TextBlock Text="{Binding FileName}"/>
	</DataTemplate>
	
	<DataTemplate x:Key="ImageFileItemTemplate">
		<Image Source="{Binding FilePath}"/>
	</DataTemplate>
</Page.Resource>

On the other hand, how would we make use of these data templates – the ListView control (as well as any other ItemsControl, by the way) only accepts one distinct DataTemplate as input to its ItemTemplate property…

This is where the Windows.UI.Xaml.Controls.DataTemplateSelector class comes in: It acts as a middleware that is asked for every list item which DataTemplate shall be used. To use it in our sample app, we need to declare our own custom selector class, e.g. called FileItemTemplateSelector:

	public class FileItemTemplateSelector : DataTemplateSelector
	{
		protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
		{
			var file = item as FileObject;
			switch (file.MimeType)
			{
				case "image/png":
				case "image/jpg":
					return ImageFileItemTemplate;
				default:
					return GeneralFileItemTemplate;
			}
		}

		public DataTemplate GeneralFileItemTemplate { get; set; }
		public DataTemplate ImageFileItemTemplate { get; set; }
	}

This snippet assumes that FileObject is the type of our file item POCOs, and that it contains a MimeType property to distinguish different types of files.

How does this template selector work? In general (for all DataTemplateSelector implementations), for each list item the SelectTemplateCore method is called, and the respective list item is passed to it. In out implementation, we cast the passed item to the FileObject type, check whether it is an image or not, and tell the template selector to use a specific DataTemplate to render the respective list item.

Where do these specific DataTemplates come from? Well, let’s take a look at how a DataTemplateSelector is referenced in the view:

<Page.Resource>
    <DataTemplate x:Key="GeneralFileItemTemplate">
        <TextBlock Text="{Binding FileName}"/>
    </DataTemplate>
	
    <DataTemplate x:Key="ImageFileItemTemplate">
        <Image Source="{Binding FilePath}"/>
    </DataTemplate>
	
    <helpers:FileItemTemplateSelector x:Key="FileItemTemplateSelector" GeneralFileItemTemplate="{StaticResource GeneralFileItemTemplate}" ImageFileItemTemplate="{StaticResource ImageFileItemTemplate}"/>

</Page.Resource>

<ListView ItemsSource="{Binding Files}" ItemTemplateSelector="{StaticResource FileItemTemplateSelector}" />

First: Declare the different template – nothing new so far. Second: Reference the template selector – I usually also do that in the Resources section, to have it as near as possible to the DataTemplate definitions – and pass the required DataTemplate resources into its public properties. Third: In the ListView, set the ItemTemplateSelector property instead of the ItemTemplate. Done!

Of course, this approach is not limited to using one of two different DataTemplates. Instead, since we manually define both the template selector’s DataTemplate properties and the SelectTemplateCore logic, we are able to reference as many different templates as we want.