Conditional Formatting of Items in an Items Control

When developing XAML based applications for Windows Store and Windows Phone we encounter situations when we are binding data with an ItemsControl and the data template needs to show data that is not the same for all the items. ListView, GridView, FlipView and ListBox are most the common ItemsControl to which this discussion is applicable.

Here in this article I will try to explain broadly two options.

1. Item Template Selector

This is the most common and obvious solution where you can define as many data templates as necessary in Resources and then implement.

This is typically how you would implement an Item Template Selector.

  1. using Windows.UI.Xaml;  
  2. using Windows.UI.Xaml.Controls;  
  3.   
  4. namespace SampleApp  
  5. {  
  6.     public class SampleTemplateSelector : DataTemplateSelector  
  7.     {  
  8.         public DataTemplate FormatOne { getset; }  
  9.         public DataTemplate FormatTwo { getset; }  
  10.         public DataTemplate Default { getset; }  
  11.   
  12.         protected override DataTemplate SelectTemplateCore(object item)  
  13.         {  
  14.             DataTemplate selectedTemplate = null;  
  15.   
  16.             if (item != null && item is NewsItemViewModel)  
  17.             {  
  18.                 NewsItemViewModel data = item as NewsItemViewModel;  
  19.   
  20.                 if (data.ConditionForFormatOne)  
  21.                 {  
  22.                     selectedTemplate = FormatOne != null ? FormatOne : Default;  
  23.                 }  
  24.                 else if (data.ConditionForFormatTwo)  
  25.                 {  
  26.                     selectedTemplate = FormatTwo != null ? FormatTwo : Default;  
  27.                 }  
  28.                 else  
  29.                 {  
  30.                     selectedTemplate = Default;  
  31.                 }  
  32.             }  
  33.   
  34.             return selectedTemplate;  
  35.         }  
  36.     }  
  37. }  
To consume this in a page we need to define an appropriate number of data templates and then use them in our selector. Here is how you would do it.
  1. <Page  
  2.     x:Name="pageRoot"  
  3.     x:Class="SampleApp.HomePage"  
  4.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  5.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  6.     xmlns:local="using:SampleApp"  
  7.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
  8.     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
  9.     mc:Ignorable="d">  
  10.     <Page.DataContext>  
  11.         <local:SampleViewModel/>  
  12.     </Page.DataContext>  
  13.     <Page.Resources>  
  14.         <DataTemplate x:Key="DefaultTemplate">  
  15.             <StackPanel Background="#FF222222" Height="250" Width="250">  
  16.                 <TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" Padding="5" Margin="0"/>  
  17.                 <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextBlockStyle}" Padding="5" Margin="0"/>  
  18.             </StackPanel>  
  19.         </DataTemplate>  
  20.         <DataTemplate x:Key="FormatOneTemplate">  
  21.             <Grid Background="#FF999999" Height="250" Width="250">  
  22.                 <Grid.RowDefinitions>  
  23.                     <RowDefinition Height="*"/>  
  24.                     <RowDefinition Height="Auto"/>  
  25.                 </Grid.RowDefinitions>  
  26.                 <Image Source="{Binding Image}" Stretch="UniformToFill" Grid.RowSpan="2"/>  
  27.                 <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}" Grid.Row="1" Padding="5" Margin="0"/>  
  28.             </Grid>  
  29.         </DataTemplate>  
  30.         <DataTemplate x:Key="FormatTwoTemplate">  
  31.             <Grid Background="Black" Height="250" Width="250">  
  32.                 <Grid.RowDefinitions>  
  33.                     <RowDefinition Height="*"/>  
  34.                     <RowDefinition Height="Auto"/>  
  35.                 </Grid.RowDefinitions>  
  36.                 <Image Source="{Binding Image}" Stretch="UniformToFill" Grid.RowSpan="2"/>  
  37.                 <HyperlinkButton NavigateUri="{Binding Uri}" Content="{Binding Name}" Grid.Row="1" Padding="5" Margin="0"/>  
  38.             </Grid>  
  39.         </DataTemplate>  
  40.         <local:SampleTemplateSelector x:Key="SampleTemplateSelector"   
  41.                                       Default="{StaticResource DefaultTemplate}"  
  42.                                       FormatOne="{StaticResource FormatOneTemplate}"  
  43.                                       FormatTwo="{StaticResource FormatTwoTemplate}"/>  
  44.     </Page.Resources>  
  45.   
  46.     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
  47.         <Grid.ChildrenTransitions>  
  48.             <TransitionCollection>  
  49.                 <EntranceThemeTransition/>  
  50.             </TransitionCollection>  
  51.         </Grid.ChildrenTransitions>  
  52.         <Grid.RowDefinitions>  
  53.             <RowDefinition Height="140"/>  
  54.             <RowDefinition Height="*"/>  
  55.         </Grid.RowDefinitions>  
  56.   
  57.         <!-- Back button and page title -->  
  58.         <Grid>  
  59.             <Grid.ColumnDefinitions>  
  60.                 <ColumnDefinition Width="120"/>  
  61.                 <ColumnDefinition Width="*"/>  
  62.             </Grid.ColumnDefinitions>  
  63.             <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"  
  64.                         Style="{StaticResource NavigationBackButtonNormalStyle}"  
  65.                         VerticalAlignment="Top"  
  66.                         AutomationProperties.Name="Back"  
  67.                         AutomationProperties.AutomationId="BackButton"  
  68.                         AutomationProperties.ItemType="Navigation Button"/>  
  69.             <TextBlock x:Name="pageTitle" Text="Sample App" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"   
  70.                         IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>  
  71.         </Grid>  
  72.   
  73.         <GridView Margin="120,0,0,0" ItemTemplateSelector="{StaticResource SampleTemplateSelector}"   
  74.                   ItemsSource="{Binding NewsFeed}" Grid.Row="1"/>  
  75.   
  76.     </Grid>  
  77. </Page>  
Pros: 
  1. This is the most obvious solution.

  2. It is a clean implementation since all the templates can be defined separately.

Cons:

  1. Item Template Selector is fired only once when your control was initially created, any subsequent change that would result in a different template will not be effective until all your ItemsControl is rendered again.

2. Value Converter

A Value Converter is primarily used to change values when displaying stuff on the screen but they are also handy in this type of situation. In this approach we mostly deal with Visibility via Boolean to Visibility or its negation and so on.

Refer to following XAML that will produce the same output as before (if the data does not change at runtime).

  1. <Page  
  2.     x:Name="pageRoot"  
  3.     x:Class="SampleApp.HomePage"  
  4.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  5.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  6.     xmlns:local="using:SampleApp"  
  7.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
  8.     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
  9.     mc:Ignorable="d">  
  10.     <Page.DataContext>  
  11.         <local:SampleViewModel/>  
  12.     </Page.DataContext>  
  13.     <Page.Resources>  
  14.         <local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />  
  15.         <DataTemplate x:Key="SingleTemplate">  
  16.             <Grid Height="250" Width="250">  
  17.                 <StackPanel Background="#FF222222"   
  18.                             Visibility="{Binding ConditionForDefault, Converter={StaticResource BooleanToVisibilityConverter}}">  
  19.                     <TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" Padding="5" Margin="0"/>  
  20.                     <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextBlockStyle}" Padding="5" Margin="0"/>  
  21.                 </StackPanel>  
  22.                 <Grid Background="#FF999999"  
  23.                       Visibility="{Binding ConditionForFormatOne, Converter={StaticResource BooleanToVisibilityConverter}}">  
  24.                     <Grid.RowDefinitions>  
  25.                         <RowDefinition Height="*"/>  
  26.                         <RowDefinition Height="Auto"/>  
  27.                     </Grid.RowDefinitions>  
  28.                     <Image Source="{Binding Image}" Stretch="UniformToFill" Grid.RowSpan="2"/>  
  29.                     <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}" Grid.Row="1" Padding="5" Margin="0"/>  
  30.                 </Grid>  
  31.                 <Grid Background="Black"  
  32.                       Visibility="{Binding ConditionForFormatTwo, Converter={StaticResource BooleanToVisibilityConverter}}">  
  33.                     <Grid.RowDefinitions>  
  34.                         <RowDefinition Height="*"/>  
  35.                         <RowDefinition Height="Auto"/>  
  36.                     </Grid.RowDefinitions>  
  37.                     <Image Source="{Binding Image}" Stretch="UniformToFill" Grid.RowSpan="2"/>  
  38.                     <HyperlinkButton NavigateUri="{Binding Uri}" Content="{Binding Name}" Grid.Row="1" Padding="5" Margin="0"/>  
  39.                 </Grid>  
  40.             </Grid>  
  41.         </DataTemplate>  
  42.     </Page.Resources>  
  43.   
  44.     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
  45.         <Grid.ChildrenTransitions>  
  46.             <TransitionCollection>  
  47.                 <EntranceThemeTransition/>  
  48.             </TransitionCollection>  
  49.         </Grid.ChildrenTransitions>  
  50.         <Grid.RowDefinitions>  
  51.             <RowDefinition Height="140"/>  
  52.             <RowDefinition Height="*"/>  
  53.         </Grid.RowDefinitions>  
  54.   
  55.         <!-- Back button and page title -->  
  56.         <Grid>  
  57.             <Grid.ColumnDefinitions>  
  58.                 <ColumnDefinition Width="120"/>  
  59.                 <ColumnDefinition Width="*"/>  
  60.             </Grid.ColumnDefinitions>  
  61.             <Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"  
  62.                         Style="{StaticResource NavigationBackButtonNormalStyle}"  
  63.                         VerticalAlignment="Top"  
  64.                         AutomationProperties.Name="Back"  
  65.                         AutomationProperties.AutomationId="BackButton"  
  66.                         AutomationProperties.ItemType="Navigation Button"/>  
  67.             <TextBlock x:Name="pageTitle" Text="Sample App" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"   
  68.                         IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>  
  69.         </Grid>  
  70.   
  71.         <GridView Margin="120,0,0,0" ItemTemplate="{StaticResource SingleTemplate}"   
  72.                   ItemsSource="{Binding NewsFeed}" Grid.Row="1"/>  
  73.   
  74.     </Grid>  
  75. </Page>  
Pros: 
  1. It responds to changes in data values.
  2. It follows a very simple fundamental process of showing and hiding elements on the screen that most people use in most cases outside of Grid/List Views.

Cons:

  1. In this approach there are actually many elements in the Visual Tree, just simply hidden and that, in some cases, can be a concern.

Next Recommended Readings