Recommended


Silverlight Hosting

Skip Navigation LinksHome / Articles / View Article

Animating ListBox items - the VisualStateManager

+ Add to SilverlightShow Favorites
3 comments   /   posted by Ivan Dragoev on Jun 13, 2008
(9 votes)

Introduction

Note: This article is updated and runs on Silverlight 2

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">
        <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>