Introduction
In the first article I showed you how to animate ListBox items in Silverlight 2 Beta 1. Then, to make all the animation the way I wanted, I used some code and hardcoded names of the Storyboards for each state. Now with Silverlight 2 Beta 2 and the cool new
VisualStateManager the task for adding animation and other effects for each state becomes easier. Other than that – now we can add animations even for the transitions from one state to another.
In this article I’ll make the same animations like in the previous one but this time using VisualStateManager and styles. I will also show you the problems and limitations I faced during the implementation.
Download source code
Overview
In the new Silverlight 2 Beta 2 the ListBox control is further improved and adding VisualStateManager in the whole picture makes states animation much easier - without any need of additional code.
I will create a new Silverlight project in VS2008 and I’ll copy the stuff that I need from the
first part – the Clients class and the Images.
The second step is to add in the Page.xaml a ListBox which I want to animate. I will also add a TextBlock to show the name of the selected client – a simple feedback showing theselected item. I’ll put both the ListBox and theTextBlock in a StackPanel:
|
<UserControl x:Class="AnimatingListBoxItems_2.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<StackPanel>
<ListBox x:Name="lbClients" Grid.Row ="0" Width="400" HorizontalAlignment="Center" VerticalAlignment="Stretch" DisplayMemberPath="Name" Style="{StaticResource ListBoxStyle}" SelectionChanged="lbClients_SelectionChanged">
</ListBox>
<TextBlock x:Name="selectedClient" Height="20" Width="400" Foreground="Black"></TextBlock>
</StackPanel>
</Grid>
</UserControl>
|
The lbClients_SelectionChanged event handler set the text of the text block:
|
selectedClient.Text = ( ( Client )e.AddedItems[ 0 ] ).Name;
|
Additionally I add a simple style for the ListBox in the application’s resources, so running the application will give you this:

Now, let’s start playing with the items. I will create new style in application resources and I’ll set it as ItemContainerStyle of the ListBox:
|
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Foreground" Value="#FF000000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="RootElement" Height="54">
<StackPanel Orientation="Horizontal" Margin="2,2,2,2" Background="Transparent">
<Border x:Name="ClientImageBorder" Background="White" CornerRadius="5">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Border.RenderTransform>
<Image Source="{Binding Thumbnail}" Width="50" Height="50" ></Image>
</Border>
<StackPanel x:Name="ClientDataPanel" Orientation="Vertical" Margin="2,2,2,2">
<StackPanel.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</StackPanel.RenderTransform>
<TextBlock x:Name="tbClientName" Text="{Binding Name}" FontSize="16" Height="22" ></TextBlock>
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0" Grid.Row="0">
<TextBlock Text="{Binding Country}" FontSize="9" />
<TextBlock Text=", " FontSize="9" />
<TextBlock Text="{Binding PostalCode}" FontSize="9" />
</StackPanel>
<TextBlock Text="{Binding Address}" FontSize="9" Grid.Column="0" Grid.Row="1"/>
<TextBlock Text="{Binding Phone}" FontSize="9" Grid.Column="1" Grid.Row="0"/>
<TextBlock Text="{Binding Email}" FontSize="9" Grid.Column="1" Grid.Row="1"/>
</Grid>
</StackPanel>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
|
|
<ListBox x:Name="lbClients" Grid.Row ="0" DisplayMemberPath="Name" Style="{StaticResource ListBoxStyle}" ItemContainerStyle="{StaticResource ListBoxItemStyle}" SelectionChanged="lbClients_SelectionChanged">
|
Running it now you will see the list of clients with some additional data:

To apply the animations I need ViewStateManager inside the style, so I’ll add a reference to the assembly in the style:
|
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
|
I will add Storyboards for several states: Focused, Unfocused, Selected, Unselected and SelectedUnfocused:
|
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver" />
<vsm:VisualState x:Name="Disabled" />
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="FocusStates">
<vsm:VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusRectangle" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Unfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="FocusRectangle" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="SelectionStates">
<vsm:VisualState x:Name="Selected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="FontSize" From="32" To="16" Duration="00:00:00.0000000"/>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="Height" From="50" To="22" Duration="00:00:00.0000000"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Unselected">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="FontSize" From="16" To="32" Duration="00:00:00.0000000"/>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="Height" From="22" To="50" Duration="00:00:00.0000000"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="SelectedUnfocused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="FontSize" From="32" To="16" Duration="00:00:00.0000000"/>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="Height" From="50" To="22" Duration="00:00:00.0000000"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
|
But why do these storyboards have duration of 0? The reason is the fact, that one action can fire more than one animation. For example, when the user selects an item, the Selected and Focused animation is applied, but when the mouse cursor steps out of the control the Unselected is applied. As a result the item is shown as unselected since it is the last animation that is applied. To avoid this, I will make the duration for all these animations to 0 and will add some transitions. Thus the transitions will deal with the animations providing the smooth transition between states. And here are the transition definitions:
|
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition To="Unselected" Duration="00:00:00.3000000">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="FontSize" From="16" To="32" Duration="00:00:00.3000000"/>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="Height" From="22" To="50" Duration="00:00:00.3000000"/>
</Storyboard>
</vsm:VisualTransition>
<vsm:VisualTransition To="Selected" Duration="00:00:00.3000000">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="FontSize" From="32" To="16" Duration="00:00:00.3000000"/>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="Height" From="50" To="22" Duration="00:00:00.3000000"/>
</Storyboard>
</vsm:VisualTransition>
<!--<vsm:VisualTransition From="SelectedUnfocused" To="Selected" Duration="00:00:00.0000000">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="FontSize" From="32" To="16" Duration="00:00:00.0000000"/>
<DoubleAnimation Storyboard.TargetName="tbClientName" Storyboard.TargetProperty="Height" From="50" To="22" Duration="00:00:00.0000000"/>
</Storyboard>
</vsm:VisualTransition>-->
</vsm:VisualStateGroup.Transitions>
|
As you can see the transitions define the same animations like in part one. All the transitions are defined to be applied from any state to Unselected and Selected. If you want you can define animation from a specific state to another state, for example from Selected to MouseOver – just add From=”<state>” where <state> is the name of the initial state.
One think doesn’t behave like I expect – when the control has the focus, the transition animation to Selected state is applied. I left the code commented and I’ll continue investigating how this could be achieved without affecting the other effects.
If you run the application now and select with mouse or keyboard, the items will be animated:
Issues
So far I’ve made animations when the item is selected or unselected. But what about have the animation on initial load? Well I didn’t manage to make it in xaml using VisualStateManager because there is no reliable state for this. What I also noticed is that when the ListBox is loaded, the Unselected state is forced. So applying animation is still possible but manually only – see the first article.
Another thing I found confusing is the sequence the state animations are called in. My expectations were to see only one state animation of a group to be applied: Selected and Focused for example. If the user clicks with the left mouse button to select an item and then move the mouse cursor, the Selected animation will be applied and when leaving – Unselected. Thus the complexity of achieving even simple effects increases.
What I prefer is to see exactly one state animation applied and many state-to state transitions. Thus the whole process will be much clearer.
Another small issue I faced, not related to ListBox item, is the missed MouseLeftButtonDown event when no background is specified. See the example
here.
Conclusion
Silverlight 2 beta 2 is definitely one step forward, but still a lot more things have to be done. Also, according to me, some nice ideas, like VSM, are implemented in a more complex way than needed. Maybe I miss something, I do not know. For sure I’ll continue digging in it ;-)
References
ListBox Class on MSDN
ListBox Styles and Templates
http://msdn.microsoft.com/en-us/library/cc278062(VS.95).aspx
Creating template control
Silverlight and the VisualStateManager
Creating Control Skins with Visual State Manager - An Introduction
Visual State Manager for User Controls: A Simple Chord Finder Example
Animating ListBoxItems using VSM
Working with VisualStateManager