Styling a ListBox aka Autopsy of a ListBox

For some reason, I keep ending up with restyled ListBoxes in my projects. I don’t know if it is because I lack imagination or if the ListBox is just such a useful control. Anyhow, it is a control that most people will use in a lot of projects and that often needs to be restyled. However useful it is, the layout is pretty stiff and Windowsy and unless you are going for that old school Windows look, you will most likely want to style it and make it a bit flashier. Unfortunately, after having had a look around the web, I still haven’t found a good resource explaining the innards of a ListBox, so I decided to create one. It might not be a complete one, but it will cover most of the things you need to make some serious layout changes… So here we go

The standard ListBox looks like this (in case you have never seen it before…and if you haven’t, where have you been for the last 15 years?)

Original

And as you can see, it looks like any ListBox in any place. But as with all controls in Silverlight, we can solve this with a bit of styling/templating. However, the template for the ListBox is not the simplest thing in the world. Not that it is complicated, but a restyle can include as much as 4 templates. The four templates I’m talking about are ListBox.Template, ListBox.ItemContainerStyle, ListBox.ItemTemplate and ListBox.ItemsPanel.

Most demos you see regarding styling the ListBox is about changing the ListBox.ItemTemplate, which is a DataTemplate. This template makes it possible to change the layout of the information inside each item, but it is kind of limiting for a couple of reasons. So I tend to end up changing more of the style. So I have decided to start at the bottom and work upwards.

The “bottom” most template is the ListBox’s ControlTemplate. The standard template for the ListBox ControlTemplate contains a Grid and 2 Borders. The Grid is the root of the template and contains the Borders that are responsible for the “real” layout. The reason for there being 2 is that one is being used for showing validation information. I will ignore this in the post. The template also contains a VisualStateManager that takes care of the ValidationStates. But let’s ignore that and focus on the “main” Border. The “main” Border in turn contains a ScrollViewer and an ItemsPresenter. If you want to create a really simple ListBox, you can replace the template with a simple on like this

<ListBox Width="300" Height="100">
<ListBox.Template>
<ControlTemplate TargetType="ListBox">
<Grid>
<Border BorderBrush="Black" Background="BurlyWood"
BorderThickness="1" Padding="3">
<ScrollViewer x:Name="ScrollViewer" Padding="3"
Background="Gold" BorderBrush="Purple"
BorderThickness="2">
<ItemsPresenter />
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</ListBox.Template>
...
</ListBox>

Which turns the the ListBox into this beautiful thing

ControlTemplate

Pardon the blinding color that make you want to poke your eyes out with a blunt object…

The ItemsPresenter that is placed inside the ScrollViewer is responsible for iterating through the items in the list. It is however completely ignorant of what it is rendering. It does however place each item inside a Panel. The standard Panel is a StackPanel that makes the items stack up nicely in nice little rows.

If you feel like changing this Panel into some other Panel type, you can do so by setting the ItemsPanel. The ItemsPanel property is of type ItemsPanelTemplate, and is solely used as the visual root for the ItemsPresenter’s items. The StackPanel is the most obvious choice in the ListBox scenario, but if you change it to a Canvas or Grid you can actually turn the ListBox into something as cool as Bea’s solar panel ListBox. Yes, that is a WPF example, but it can be created in Silverlight as well…I assume, I haven’t actually tried… Yet…

In my ugly demo ListBox, I will keep the StackPanel, but at least show how to exchange it

<ListBox Width="300" Height="100">
...
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Turquoise" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

 

Which gives us this gorgeous layout

ItemsPanel

That’s 2 down…and 2 to go… The next level up is the ItemContainerStyle. In general, even if just doing smaller modifications to a ListBox, I tend to change the ItemContainerStyle. I often change the ItemContainerStyle instead of changing the ListBox.ItemTemplate. And why would I do this? Well, the ItemContainerStyle is a “real” Style, with all of the bells and whistles. The Style has a TargetType of ListBoxItem. But what are all the “bells and whistles” that makes me change the ItemContainerStyle instead of just the ItemTemplate. Well, mainly it is the VisualStateManager. The ItemContainerStyle supports using the VSM, while the ItemTemplte doesn’t. So if you want to make any layout changes based on the items state, you have to change the ItemContainerStyle instead. The standard template consists of a Grid, a couple of Rectangles, a VisualStateManager and a ContentPresenter. The ContentPresenter’s ContentTemplate is in turn bound to with a TemplateBinding to the ListBoxItem’s ContentTemplate. A really simple restyle, removing almost all of it to gain readability, looks like this

<ListBox Width="300" Height="100">
...
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="Red" Margin="3">
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
HorizontalAlignment="Stretch" Margin="3"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

 

And makes the ListBox look like this

ItemContainerStyle

When creating an ItemContainerStyle template, you can skip the ContentPresenter and create your data layout at this level instead. It gives you, as I said before, the flexibility to work with the VSM and modify what is shown while the item is active/inactive. Remember that the template can for example expand if you want that. Just add a Panel of some sort and set its Visibility using the VSM to get an expanding item. An example of this is available at the www.office2010themovie.com site that I built a while back. Unfortunately, none of my recent work is publicly available at this time…

But if you are ok with the look and feel of the ItemContainerStyle and just want to lay out the data differently, or decide to use a ContentPresenter in your custom ItemContainerStyle, you need to set the ItemTemplate. The ItemTemplate is a DataTemplate. That means that you can only layout your controls inside the item with this, and bind it to the current item through data bindings. In this case, I have created a really simple DataTemplate that looks like this

<ListBox Width="300" Height="100">
...
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Gray">
<TextBlock Text="{Binding}" Foreground="White" Margin="6,3" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

 

Which completes the ugly ListBox example and creates the final piece of the puzzle that makes up this fantastic view

ItemTemplate

Once again, I apologize for the fuglyness, but I added the ugly colors so that you can see all of the individual pieces of the control even when fully assembled.

You could of course go even further and restyle the ScrollViewer as well, giving it an even more custom look.

I hope this is helpful in your future styling endeavors and that you post any comments or questions…

Cheers!

Pingbacks and trackbacks (1)+

Comments are closed