Specifying the Selection Color, Content Alignment, and Background Color for items in a ListBox
This post covers some tips and tricks that people frequently ask concerning specifying the appearance of a ListBox. Here’s some things that someone, somewhere, might find useful:
Specifying the Selection Color
If you’ve ever tried the following, you know it won’t work.
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="LightGreen" />
</Trigger>
</Style.Triggers>
</Style>
This doesn’t work because the control template of the ListBoxItem changes the background of an Border that is internal to the template, not the background of the ListBoxItem itself. The control template uses the color defined by SystemColors.HighlightBrush. Luckily, the SystemColors.HighlightBrush has a key associated with it, so you can create a Brush with its key set to SystemColors.HighlightBrushKey to change the color of a selected ListBoxItem:
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen"/>
</Style.Resources>
</Style>
You might be thinking, “That’s great, but when the control loses focus, the selection still turns to the default color (usually gray).” Never fear, the background for selected items in an inactive ListBox is specified by SystemColors.ControlBrush and you can use the SystemColors.ControlBrushKey to specify a different color. The following style causes the select items in a ListBox to be LightGreen when the ListBox has focus and LightBlue when it doesn’t have focus.
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightBlue"/>
</Style.Resources>
</Style>
Aligning Content in a ListBox
Often you want the content of a ListBox to horizontally span the row. The Data Templating Overview discusses this, but it comes up frequently so it bears repeating. Suppose your ListBox is bound to a list of items to purchase. Perhaps you want the name of the item on the left and the price of the item below the name and to the far right. Your data template might look like this:
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding XPath=@name}"/>
<TextBlock Text="{Binding XPath=@price}"
Grid.Row="1" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
This gives you the following:
As you can see, the TextBlock for the price is right-aligned, but it isn’t aligned with the edge of the ListBox. This is because the TextBlock is right-aligned with the Grid in the DataTemplate and the Grid sizes to its content To have the Grid span the width of the ListBox. Set the ListBox.HorizontalContentAlignment property to Stretch. Then the price will align to the right edge of the ListBox.
Making the Background Span the Entire Row
Setting the ListBox.HorizontalContentAlignment property to Stretch also allows the Background brush to paint the entire row of a ListBox. Suppose you want to bind the Background to a data source or use a data trigger to change the background of an item. The following data template changes the background on an item to red if it is sold.
<DataTemplate>
<Grid Name="grid1" Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding XPath=@name}"/>
<TextBlock Grid.Row="1"
Text="{Binding XPath=@price}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding XPath=@sold}" Value="true">
<Setter TargetName="grid1" Property="Background" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
If you don’t set the HorizontalContentAlignmentProperty, the ListBox looks likes the following:
That’s probably not what you want. When ListBox.HorizontalContentAlignment equals to Stretch, the background paints the entire row.
Comments
Anonymous
September 08, 2007
Hello, useful tips, indeed. However, when you set the HighlightBrushKey to the Transparent or some light color, it is likely you also wan't to change the text color. What's the key here? ;-) Second, if I just want to turn off such highlighting, is there any better way then setting HighlightBrushKey and ControlBrushKey to Transparent? Thanks, Jan.Anonymous
September 10, 2007
Hi Jan, Good questions. Regarding the foreground, the following keys are used: Key when the ListBox has focus: SystemColors.HighlightTextBrushKey Key when the ListBox does not have focus: SystemColors.ControlTextBrushKey But the ListBoxItem control template changes the Foreground property of the ListBoxItem, so you can also just use a Trigger to change the color of the Foreground. <Style TargetType="ListBoxItem"> <Style.Resources> <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen"/> <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightBlue"/> </Style.Resources> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Foreground" Value="Red" /> </Trigger> </Style.Triggers> </Style> To turn of highlighting, I believe using the Transparent brush is easiest. Another option is to redefine the control tenplate of the ListBoxItem and remove the triggers for IsSelected. Here’s the ControlTemplate defined in WPF. I’ve marked which triggers should be removed to disable selection highlighting. <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true"> <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </Border> <ControlTemplate.Triggers> <!--REMOVE THIS TRIGGER--> <Trigger Property="IsSelected" Value="true"> <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/> </Trigger> <!--REMOVE THIS TRIGGER--> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsSelected" Value="true"/> <Condition Property="Selector.IsSelectionActive" Value="false"/> </MultiTrigger.Conditions> <Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/> <Setter Property="Foreground Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> </MultiTrigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> Note that you could also redefine the LlistBoxItem ControlTemplate to change the color of the background of the ListBoxItem by changing the Values in the setters above...but the point of the post was to show how change the color without having to redefine the ControlTemplate.Anonymous
December 06, 2007
The comment has been removedAnonymous
December 26, 2008
В очередной раз получил данный вопрос и решил поделиться ответом со всеми.   Как я думаю, многиеAnonymous
April 20, 2009
Hello again! Could this trick be used to replace one brush with another? I'm trying to implement HideSelection=False on a ListBox. The easiest I could come up with is: <Style.Resources> <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{x:Static SystemColors.HighlightColor}" /> </Style.Resources> But this creates solid brush even if HighlightBrush wasn't solid. Is there any way how to do this while preserving the whole brush type and its properties? Thanks! JanAnonymous
April 24, 2009
Hi Jan, Sorry for the delay in getting back to you. If I undersstand your question, in some place the ControlBrush is used, but you want the HighlightBrush to be used instead. Basically, you want a copy of the HighlightBrush and give it the key ControlBrushKey. There isn't a way to do this. In this case, you have to copy the control template and change the references from the ControlBrush to the HighlightBrush. Sorry I don't have better news for you. CaroleAnonymous
April 26, 2009
Hi Carole, Yes you understand the question well. So it's basically a choice whether to lock brush type or listbox template from being updated with future framework updates. I guess that fixing the brush type to solid is a bit better choice. Anyway, thank you for your response! At least I know I don't miss anything. JanAnonymous
July 27, 2009
The comment has been removedAnonymous
January 21, 2010
This is a really great tip. But if I don't work for Microsoft how exactly was I suppose to know how to do this?Anonymous
January 21, 2010
The comment has been removed