Individual XAML list item styling

Remember my blog post about automatically choosing appropriate DataTemplates for an ItemsControl (such as a ListView) by using a DataTemplateSelector? This certainly is useful for items that shall be visualized using different controls, but definitely overkill for scenarios that just need different styling of different items. If you ever find yourself in such a scenario, it’s good to know that the same approach also works with styles instead of data templates.

Let’s think this through by implementing a common sample application: We all know (and probably use everyday) messenger apps. They typically visualize messages as part of a conversation, meaning even historical messages are shown up to a certain date in the past:

Messaging app screen shot

From a developer’s point of view, we’d probably use some kind of list view to implement a chronological list of messages like this. However, there are two types of messages: Sent messages are displayed differently than received ones.

public class Message
{
    public string Content { get; set; }
}

public class SentMessage : Message
{
    public DateTime SentTimestamp { get; set; }
}

public class ReceivedMessage : Message
{
    public DateTime ReceivedTimestamp { get; set; }
    public string SenderName { get; set; }
}

At this point, you might think ‘good to know that there is a DataTemplateSelector that allows me to specify two types of control templates for a single list view!’ – but let’s think this through first, is this really the optimum solution?

Both types of messages can contain text – what would the two data templates look like? They would probably both contain a label control with some styling applied for background color, left / right alignment, etc. (You might argue that in modern messaging apps, messages do not only consist of text but may contain images, audio messages, videos, etc. as well, so we would need additional controls rather than a simple text label. However, all this applies to both sent and received messages, so both data templates would need to contain all these different controls.) This leads us to the conclusion: If two data templates contain the same set of user controls, separating them is not the best approach.

Instead of separating the two types of list items into two different data templates (which would look nearly the same except for the styling), separate semantics (the user controls used to output data) from the visualization (the actual look-and-feel of these controls)! In our case, both message types can use the same controls since both just need to output text:

<ListView ItemsSource="{Binding AllMessages}">
	<ListView.ItemTemplate>
		<DataTemplate>
			<TextBlock Text="{Binding Content}"/>
		</DataTemplate>
	</ListView.ItemTemplate>
</ListView>

The only thing that’s different between the two message types is the visualization, so let’s create to simple styles in the page’s resources (or wherever you want to store them):

<Page.Resource>
	
<Style x:Key="SentMessageStyle" TargetType="ListViewItem">
		<Setter Property="HorizontalContentAlignment" Value="Right" />
		<Setter Property="Background" Value="LightGreen"/>
	</Style>

	
	
<Style x:Key="ReceivedMessageStyle" TargetType="ListViewItem">
		<Setter Property="HorizontalContentAlignment" Value="Left" />
		<Setter Property="Background" Value="LightGray"/>
	</Style>

</Page.Resource>

Assigning these styles to the actual list items work in a similar way as assigning data templates to a list view. First, we create a StyleSelector class that handles the decision logic:

	public class MessageStyleSelector : StyleSelector
	{
		protected override Style SelectStyleCore(object item, DependencyObject container)
		{
			if (item is SentMessage)
				return SentMessageStyle;
			else
				return ReceivedMessageStyle;
		}

		public Style SentMessageStyle { get; set; }
		public Style ReceivedMessageStyle { get; set; }
	}

You’ll recognize a lot of things from the DataTemplateSelector sample in this class: We define (an arbitrary amount of) properties for referencing the target styles, and override the SelectStyleCore method which receices each list item and decides which style is appropriate. Even using this from within XAML looks very similar:

<Page.Resource>
	
<Style x:Key="SentMessageStyle" TargetType="ListViewItem">
		<Setter Property="HorizontalContentAlignment" Value="Right" />
		<Setter Property="Background" Value="LightGreen"/>
	</Style>

	
	
<Style x:Key="ReceivedMessageStyle" TargetType="ListViewItem">
		<Setter Property="HorizontalContentAlignment" Value="Left" />
		<Setter Property="Background" Value="LightGray"/>
	</Style>

	
	<helpers:MessageStyleSelector x:Key="MessageStyleSelector" SentMessageStyle="{StaticResource SentMessageStyle}" ReceivedMessageStyle="{StaticResource ReceivedMessageStyle}"/>

</Page.Resource>

<ListView ItemsSource="{Binding AllMessages}" ItemContainerStyleSelector="{StaticResource MessageStyleSelector}">
	....
</ListView>

Why is this the better solution? Well, besides all the fuss about architecture and separation of concerns, there is one practical reason: Image we’d want to extend our simple messaging app to allow sending images instead of texts. When using different data templates, we’d need to replace the TextBlock control with an Image element in both templates, which is both annoying as it means additional effort and opens up potential copy-and-paste errors!

Leave a Comment

Your email address will not be published. Required fields are marked *