| « This is your brain on .NET... any questions? | PrintTicket provider failed to retrieve PrintCapabilities » |
Background
In WPF, there are often many ways of accomplishing the same end result. I recommend always looking for the most "correct" solution, and that in my opinion is the one that most naturally fits in with the framework hooks already provided, as well as being the most generic and reusable solution possible.
The Problem
Simple. How do I implement alternating row styles in a ListBox?
Building Blocks
The following, provided in the framework, will be useful:
My Solution
You may have noticed that the relevant framework hooks do not refer to the ListBox class, but rather the shared base of ItemsControl.
So while we could write an alternating row style selector for a ListBox, why not write a more generic item style selector for any ItemsControl? (The term "item" is important even within the context of only the ListBox, because a ListBox need not even be displayed in a row format; it can in fact be styled to render in any number of configurations.)
Note that because of the way styles work in WPF, we will have to specialize the Style instances themselves when we use the selector. We'll see that later.
Implementing the StyleSelector
All we really require of the StyleSelector is to hold two styles and switch between them for every other item in the ItemsControl.
using System.Windows; using System.Windows.Controls; public class ItemsControlAlternatingStyleSelector : StyleSelector { Style evenStyle, oddStyle; public Style EvenStyle { get { return evenStyle; } set { evenStyle = value; } } public Style OddStyle { get { return oddStyle; } set { oddStyle = value; } } public override Style SelectStyle(object item, DependencyObject container) { ItemsControl control = ItemsControl.ItemsControlFromItemContainer(container); return (0 == (control.Items.IndexOf(item) % 2)) ? EvenStyle : OddStyle; } }
Defining the Styles
Now we must write styles that are specific to the container type to which the styles are to be applied. This means that for a ListBox, we are targeting its container type, ListBoxItem.
<Style x:Key="ListBoxItemEvenStyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="LightGray"/>
<Setter Property="BorderThickness" Value="0.5"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
<Style x:Key="ListBoxItemOddStyle" TargetType="ListBoxItem" BasedOn="{StaticResource ListBoxItemEvenStyle}">
<Setter Property="Background" Value="LightCyan"/>
</Style>
Note that I based the odd style on the even style. I recommend this always for minimizing duplicated style properties, because then in the odd style you need only override the properties you wish to differ from the even style.
Putting It Together
Now we need only assemble the previous two pieces.
<ItemsControlAlternatingStyleSelector
x:Key="ListBoxItemAlternatingStyleSelector"
EvenStyle="{StaticResource ListBoxItemEvenStyle}"
OddStyle="{StaticResource ListBoxItemOddStyle}"
/>
Once we've done this, we can apply the style selector to any ListBox.
<ListBox ItemContainerStyleSelector="{StaticResource ListBoxItemAlternatingStyleSelector}">
...
</ListBox>
That's it! Alternating row styles for a ListBox! In the same fashion, you could create styles for ListViewItem and apply them to a ListView with another ItemsControlAlternatingStyleSelector instance.
Code Listing
You can download the full code solution here.
This post has 366 feedbacks awaiting moderation...