(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Animating ListBox Items - the VisualStateManager

(9 votes)
Ivan Dragoev
>
Ivan Dragoev
Joined Oct 25, 2007
Articles:   7
Comments:   9
More Articles
4 comments   /   posted on Jun 13, 2008
Categories:   Controls , Design

This article is compatible with the latest version of Silverlight.


Introduction


In the first article I showed you how to animate ListBox items in Silverlight. Then, to make all the animation the way I wanted, I used some code and hardcoded names of the Storyboards for each state. From Silverlight 2 on and the cool new VisualStateManager the task for adding animation and other effects for each state becomes easier. Other than that  – 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

VisualStateManager makes states animation much easier - without any need of additional code.
I will create a new Silverlight project in Visual Studio 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>
 
    <vsm:VisualTransition To="Selected">
        <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.

References

ListBox Class on MSDN
http://msdn.microsoft.com/en-us/library/system.windows.controls.listbox(VS.95).aspx
 
ListBox Styles and Templates
http://msdn.microsoft.com/en-us/library/cc278062(VS.95).aspx
 
Creating template control
http://msdn.microsoft.com/en-us/library/cc278064(VS.95).aspx
 
Silverlight and the VisualStateManager
http://timheuer.com/blog/archive/2008/06/04/silverlight-introduces-visual-state-manager-vsm.aspx
 
Creating Control Skins with Visual State Manager - An Introduction
http://electricbeach.org/?p=98
 
Visual State Manager for User Controls: A Simple Chord Finder Example
http://electricbeach.org/?p=107
 
Animating ListBoxItems using VSM
http://leeontech.wordpress.com/2008/06/11/animating-listboxitems-using-vsm/
 
Working with VisualStateManager
http://leeontech.wordpress.com/2008/06/09/working-with-visualstatemanager/
 

Subscribe

Comments

  • -_-

    RE: Animating ListBox items - the VisualStateManager


    posted by MrCyprom on Jul 20, 2008 09:49

    Hi,

    When I set programmatically the selectedIndex the selectedstate is not applied. Did you meet this issue too ?

    Regards

  • -_-

    RE: Animating ListBox items - the VisualStateManager


    posted by Rohit on Jan 06, 2009 10:59

    Hello,

     i am using listbox in which we want to highlighted the selected items(more than 1).

    so how it possible?

    Thanks

    Rohit

  • idragoev

    RE: Animating ListBox items - the VisualStateManager


    posted by idragoev on Jan 07, 2009 08:30

    Hi Rohit,

    I think the ListBox currently doesn;t support more than one selected items.

  • -_-

    RE: Animating ListBox Items - the VisualStateManager


    posted by fdfd on Jan 18, 2011 08:26
    大幅度

Add Comment

Login to comment:
  *      *