Select Page

The DataTemplateSelector could be a cool tool for changing out which DataTemplate is used to display an element within a list.  I started using it for a media feature to figure out whether an element’s thumbnail should display as image, sound, or video.

The Usual

Typically, this process involves creating a DataTemplateSelector class like this:

public class LibraryItemDataTemplateSelector : DataTemplateSelector
{
   public DataTemplate AudioDataTemplate { get; set; }
   public DataTemplate ImageDataTemplate { get; set; }
   public DataTemplate VideoDataTemplate { get; set; }
   protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
   {
      if (item == null) throw new ArgumentNullException("item");
      switch (FileHelper.GetFileKind(asset.FileName))
      {
         case FileHelper.FileKinds.AUDIO: return AudioDataTemplate;
         case FileHelper.FileKinds.IMAGE: return ImageDataTemplate;
         case FileHelper.FileKinds.VIDEO: return VideoDataTemplate;
      }
   }
}

Then, I go into my XAML and define a few DataTemplates – can be in the View’s Resources or in a separate ResourceDictionary.  Might look like this:

<DataTemplate x:Key="LibraryItemAudioDataTemplate">
   <Border>
      ...
   </Border>
</DataTemplate>
<DataTemplate x:Key="LibraryItemImageDataTemplate">
   <Border>
      ...
   </Border>
</DataTemplate>
<DataTemplate x:Key="LibraryItemVideoDataTemplate">
   <Border>
      ...
   </Border>
</DataTemplate>

Then I define the instance of the DataTemplateSelector directly in my View:

<LibraryItemDataTemplateSelector
x:Key=”LibraryItemDataTemplateSelector”
AudioDataTemplate=”{StaticResource LibraryItemAudioDataTemplate}”
ImageDataTemplate=”{StaticResource LibraryItemImageDataTemplate}”
VideoDataTemplate=”{StaticResource LibraryItemVideoDataTemplate}”
/>

And finally, I go to the place where I am displaying the items in the XAML and reference the DataTemplateSelector:

<GridView
ItemsSource=”{Binding Assets}”
ItemTemplateSelector=”{StaticResource LibraryItemDataTemplateSelector}”
/>

The Problem

The issue here comes in when the data that is being displayed changes.  More specifically, if element is added or removed from the Asset list, everything works fine.  But if a Property on an Asset is changed in-place, the SelectTemplateCore function is never triggered again to reanalyze.  This is a problem when the meat of that function analyzes the Property in question.

The Solution

Actually, more of a workaround.  A number of places discussed the issue and offered suggestions:

They all make the suggestion on recreating the instance of the DataTemplateSelector.  True that it works, but I did not like it for these reasons:

  1. I want to define my DataTemplates in the XAML.
  2. My ViewModel does not know about my View to reference the DataTemplates.

What I do is create a Property on the ViewModel with backing storage like this:

public PinToggleDataTemplateSelector DigitalPwmOutputDataTemplateSelector
{
   get
   {
      return new MyDataTemplateSelector()
      {
         DataTemplate1 = _myDataTemplateSelector.DataTemplate1,
         DataTemplate2 = _myDataTemplateSelector.DataTemplate2,
      };
   }
   set { SetProperty<MyDataTemplateSelector>(ref _myDataTemplateSelector, value); }
}
private MyDataTemplateSelector _myDataTemplateSelector;

Then I go to my View’s code-behind and, in the constructor, set the ViewModel’s DataTemplateSelector with the one defined in the XAML, like this:

vm.MyDataTemplateSelector = _xamlDataTemplateSelector;

With this approach, I am handing both problems – keeping the definition of DataTemplates in XAML and pushing to the ViewModel instead of pulling.  On top of that, I do not break the MVVM concept because the code-behind forwards directly to the ViewModel.