Styling a ListBox with Silverlight 2 Beta 2 (Part 3) - ListBoxItem Style
This is the third and final part of my updated series on how to style a ListBox in Silverlight 2 Beta 2. Here are the links to part 1 and part 2.
So far we have customised the appearance of a ListBox
, including the ScrollBars
. The one remaining missing piece is some kind of indication of which items are selected, and visual feedback when the mouse hovers over a ListBox item.
To achieve this we need to add some Storyboards
to the custom ListBoxItem
we created in my first post. There are eight states you can specify Storyboards
for. The most important for our purposes are "Normal", "Selected
", and "MouseOver
". Note that we need to use two Visual State Groups. One will define Normal
and MouseOver
, while the other will define Selected
and Unselected
. This gives us the flexibility to combine animations if we require to show combined states such as Selected
and MouseOver
.
Unfortunately it can also mean conflict of interests. With the beta 1 model, I quite easily specified one colour for Normal
, one colour for Selected
and one colour for MouseOver
. However, with the new model, the MouseOver
and Unselected
states compete to set the background colour. The solution I eventually came up with was to have two borders and control their opacity (partly inspired by this forum post). One border is used to show MouseOver
state, and the other to show Selected
state. Here is the updated ListBoxItem
style containing the new border elements (without Visual State Groups):
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
<Setter Property="Foreground"
Value="{StaticResource ListBoxItemForegroundBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="Root">
<vsm:VisualStateManager.VisualStateGroups>
...
</vsm:VisualStateManager.VisualStateGroups>
<Border CornerRadius="5" Margin="1"
x:Name="HoverBorder">
<Border.Background>
<SolidColorBrush
x:Name="HoverBorderBackgroundBrush"
Color="#51615B" />
</Border.Background>
</Border>
<Border CornerRadius="5" Margin="1"
x:Name="SelectedBorder">
<Border.Background>
<SolidColorBrush
x:Name="SelectedBorderBackgroundBrush"
Opacity="0"
Color="#397F8F" />
</Border.Background>
</Border>
<ContentPresenter
Margin="4,1"
Content="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have chosen to prioritise the Selected
background above the MouseOver
background, but you can easily change that by swapping the order of the borders.
The VisualStateGroups
specify the two ColorAnimations
I need - one for Selected
and one for MouseOver
. I also needed to add a DoubleAnimation
for the opacity of the SelectedBorder
.
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="SelectionStates">
<vsm:VisualState x:Name="Unselected" />
<vsm:VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="SelectedBorderBackgroundBrush"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="1.0"/>
<ColorAnimation
Storyboard.TargetName="SelectedBorderBackgroundBrush"
Storyboard.TargetProperty="Color"
Duration="0"
To="#FF397F8F"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Normal" />
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="HoverBorderBackgroundBrush"
Storyboard.TargetProperty="Color"
Duration="0"
To="#898A8A"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
Here's what it looks like (item 3 selected, item 5 mouse over):
OK, so my choice of colours is awful and there are lots of visual elements that could be improved (and we still await a fix for the vertical scrollbar bug), but hopefully I have demonstrated how it is possible to completely change every visual element of how your ListBox is rendered in Silverlight 2 beta 2.
I have uploaded my test project here which you can load in Expression Blend 2.5 or Visual Studio 2008.
Comments
Hello,
CharlesYour article is really interesting. I have needed to know how customize my Listbox (and specially the scrollbar), and with your help i can do it!
However I have a problem with your solution. In fact, if I open it with Visual Studio 2008, it works (Designer and Test Page). But, when I open it with Blend, I can't view the design result, the exception is :
Error HRESULT E_FAIL has been returned from a call to a COM component.
After some search, I find the line which raise the problem :
ScrollBar
x:Name="HorizontalScrollBar" Style="{StaticResource ScrollBarStyle1}"
{...}
/>
If I delete the Style propertie
ScrollBar
x:Name="HorizontalScrollBar"
{...}
/>
It works but I lost the bar style ...!! I don't understand because the VerticalScrollbar Works fine while it uses the same style !!
I want to know if you are the same problem.
Thanks
Hi Charles,
Mark Hyes I have the same issue in Blend. Hopefully Microsoft will address this in a future preview release.
Thanks for your answer. I hope too that the next Blend's version will fix some bugs.
CharlesHowever this bug is strange because we can change the Vertical Style but not the Horizontal without bug!
I find a solution to bypass the problem :
CharlesWe must create 2 style for the 2 scrollbar (even if the style must be the same)
In this case, Blend can draw your control.
Mark,
RickThanks for your articles about styling a Silverlight ListBox. I am trying to incorporate your code into a demo that I am preparing. I took your styles from app.xaml and copied them into mine, and copied the ListBox from your app into mine. Everything works except that the list item foreground text is black, instead of the color that is specified in the style (NoodleLove, I believe). I've been beating my head on this for a couple of hours without success...Any thoughts?
Thanks,
Rick
Hello,
taniosVery interesting article!
The sample "TestListBoxStyle.zip" is not available anymore for download. Could you re-post it?
Thank you,
hi tanios,
Mark HI have posted a new link
I was wondering if you could help me. I'm trying specifically to isolate the color that appears as the result of mousing over a selectable item. I want it to be selectable, but I'd like for the color to not be displayed, Thanks!
Jason GrigelyHi Jason,
Mark Hthat's the HoverBorderBackgroundBrush. Simply get rid of the animation
Thanks Mark, so how would I go about applying this to every element in my page? Sorry I'm somewhat new to Silverlight.
Jason GrigelyThe style should be put in app.xaml
Mark HThen you can reference it as a StaticResource to set the Style on any ListBoxes you use
Hello
AnonymousThis link is not working i mean the download link
Hi Anonymous, I've updated the download link
Mark HAwesome work, this is exactly what I was trying to figure out since WPF styles do not copy well due to SL restrictions.
OndrejWorks with SL3 flawlessly!