Alternating Row Color in Windows Store ListView

I’ve always been drawn to XAML for its powerful ability to customize design to any extent. On a recent Windows Store project I found it difficult to create the alternating row color, or zebra striping, effect on a ListView control, something that’s proven to be easy with CSS3. I decided to write this post in hopes that others would find this technique useful.

I search online and found a technique that included adding an index property to the model in question and then adding a converter to the DataTemplate. This wasn’t ideal because it only changed the contents of the list item, so depending on padding and content alignment you’d see gaps around the row background. I also didn’t like the code smell of modifying my data model objects with UI code.

What I wanted was to override the ListViewItem control’s Background property. You could use the ListView.ItemContainerStyle property and set the background using a style, but unfortunately the style’s context wouldn’t have any way of knowing the index of the item relative to the full source collection.

The option I used was to override a ListView control and create an AlternatingRowListView. Then by overriding the PrepareContainerForItemOverride, which is responsible for the setting up the ListViewItem control after it’s be created, you could modify the background color.

public class AlternatingRowListView : ListView
{

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
        var listViewItem = element as ListViewItem;
        if (listViewItem != null)
        {
            var index = IndexFromContainer(element);

            if ((index + 1) % 2 == 1)
            {
                listViewItem.Background = new SolidColorBrush(Colors.White);
            }
            else
            {
                listViewItem.Background = new SolidColorBrush(Colors.Gray);
            }
        }
        
    }
}

This code uses a method on the ListView base class called ItemFromContainer(), which simply gives you the index of the item relative to the ListView’s source collection. With that index you can set the ListViewItem’s Background color based on a modulus of 2. Although, using hard coded colors for the background doesn’t make it very usable across other designs. This is where dependency properties come into play. They allow the control to expose properties to XAML, where you can set them to any Brush or Resource you’d like. To do this you have to specify them as they are below and update your overridden method from before with these new properties.

public class AlternatingRowListView : ListView
{
	public static readonly DependencyProperty OddRowBackgroundProperty =
		DependencyProperty.Register("OddRowBackground", typeof(Brush), typeof(AlternatingRowListView), null);
	public Brush OddRowBackground
	{
		get { return (Brush)GetValue(OddRowBackgroundProperty); }
		set { SetValue(OddRowBackgroundProperty, (Brush)value); }
	}
	
	public static readonly DependencyProperty EvenRowBackgroundProperty =
		DependencyProperty.Register("EvenRowBackground", typeof(Brush), typeof(AlternatingRowListView), null);
	public Brush EvenRowBackground
	{
		get { return (Brush)GetValue(EvenRowBackgroundProperty); }
		set { SetValue(EvenRowBackgroundProperty, (Brush)value); }
	}

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        base.PrepareContainerForItemOverride(element, item);
        var listViewItem = element as ListViewItem;
        if (listViewItem != null)
        {
            var index = IndexFromContainer(element);

            if ((index + 1) % 2 == 1)
            {
                listViewItem.Background = OddRowBackground;
            }
            else
            {
                listViewItem.Background = EvenRowBackground;
            }
        }
        
    }
}

After this, you can put it all together by using your new control in place of a ListView.

<Page
    x:Name="pageRoot"
    x:Class="ZebraStripedListView.ItemsPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="using:ZebraStripedListView.Controls">

	<controls:AlternatingRowListView 
			ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
			OddRowBackground="{ThemeResource GridOddRowBackgroundBrush}" EvenRowBackground="{ThemeResource GridEvenRowBackgroundBrush}">
		<controls:AlternatingRowListView.ItemTemplate>
			<DataTemplate>
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="300" />
						<ColumnDefinition Width="*" />
					</Grid.ColumnDefinitions>
					<TextBlock Text="{Binding Title}" Grid.Column="0" />
					<TextBlock Text="{Binding Subtitle}" Grid.Column="1" />
				</Grid>
			</DataTemplate>
		</controls:AlternatingRowListView.ItemTemplate>
	</controls:AlternatingRowListView>
</Page>

This is what the final result looks like for my sample code.

Zerbra Striped ListView

I’ve uploaded the full code to my GitHub account at http://github.com/bendewey/ZebraStripedListView. I hope this is useful to other who encounter a similar scenario.

Related Posts

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>