Silverlight 2 : petit coup d'œil au VisualStateManager
La grande nouveauté de Silverlight 2 beta 2, c’est le VisualStateManager. Dans WPF, les animations d’un ControlTemplate étaient relativement compliquées a effectuer : il fallait définir un trigger puis un storyboad, le plus souvent en écrivant du markup puisque Blend ne gérait pas tous les types de triggers. De plus la complexité des triggers augmentait avec le nombre d’états possibles pour le contrôle.
Silverlight 2 beta 2 introduit un concept beaucoup plus simple et efficace dans la plupart des scénarios. Le VisualStateManager, comme son nom l’indique est basé sur une approche orientée états. Son fonctionnement est tellement plus pratique que l'approche par triggers de WPF que le VisualStateManager va être intégré à WPF plus tard cette année.
Le principe est le suivant : chaque contrôle définit un certain nombre d’états par des attributs. Par exemple, le bouton définit les états suivants :
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusStates")]
[TemplateVisualState(Name = "MouseOver", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Pressed", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Focused", GroupName = "FocusStates")]
[TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")]
[TemplateVisualState(Name = "Normal", GroupName = "CommonStates")]
Chaque état appartient à un groupe, et à chaque instant, le contrôle est dans un et un seul état dans chaque groupe (par exemple Unfocused et Disabled).
Pour chaque état, il est possible de définir un storyboard. Ce storyboard sera joué lorsque le contrôle entrera dans cet état. De plus il est possible de définir une durée de transition entre chaque pair d'états. Lorsque le contrôle passera d'un état A à un état B, une animation linéaire de transition de la durée choisie sera jouée, et le storyboard de l'état B sera lancé lorsque l'animation de transition sera terminée.
Tout cela est entièrement définissable par Blend dans la partie gauche de l'interface :
Il est également possible d'utiliser le VisualStateManager pour un UserControl.
J'ai développé une petite application à l'occasion d'une démo sur le VisualStateManager. Il s'agit d'une implémentation du Jeu de la Vie de Conway.
Le user control CellControl est une simple ellipse, avec une opacité de 0 par défaut (donc invisible) :
<UserControl x:Class="GameOfLife.CellControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
<Ellipse HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#FF12BB05" Stroke="#FF000000" Opacity="0" x:Name="ellipse" MouseMove="OnMouseMove"/>
</UserControl>
Il utilise deux états : Alive et Dead. Il est très facile de définir des storyboards pour ces deux états en utilisant la propriété attachée VisualStateManager.VisualStateGroups de l'élément racine de l'UserControl. Dans notre cas, j'ai utilisé les storyboards suivants :
Pour l'état Alive, je veux que la cellule devienne visible. Blend génère par défaut une animation de 1 milliseconde vers la valeur voulue (opacité de 1). Je veux également que la couleur oscille. Le storyboard utilisé pour l'état Alive est donc le suivant :
<vsm:VisualState x:Name="Alive">
<Storyboard>
<DoubleAnimation BeginTime="00:00:00" To="1" Duration="00:00:00.001" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.Opacity)"/>
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" AutoReverse="True" RepeatBehavior="Forever">
<SplineColorKeyFrame KeyTime="00:00:00" Value="#FF13A108">
<SplineColorKeyFrame.KeySpline>
<KeySpline ControlPoint1="0.5,0" ControlPoint2="0.5,1"/>
</SplineColorKeyFrame.KeySpline>
</SplineColorKeyFrame>
<SplineColorKeyFrame KeyTime="00:00:00.3" Value="#FF063503">
<SplineColorKeyFrame.KeySpline>
<KeySpline ControlPoint1="0.5,0" ControlPoint2="0.5,1"/>
</SplineColorKeyFrame.KeySpline>
</SplineColorKeyFrame>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
Pour l'état Dead, je veut juste que la cellule ait la même apparence que l'apparence de base, je laisse donc le storyboard vide :
<vsm:VisualState x:Name="Dead">
<Storyboard />
</vsm:VisualState>
Il ne reste plus qu'a définir les temps de transition entre les deux états :
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition Duration="00:00:00.1" To="Alive"/>
<vsm:VisualTransition Duration="00:00:00.25" To="Dead"/>
</vsm:VisualStateGroup.Transitions>
Comme nous venons de le voir, l'aspect des deux états est entièrement défini dans le markup. La séparation entre logique et design est donc parfaite. Il ne nous reste plus qu'à pouvoir changer d'état depuis le code. Il est très facile de faire cela :
VisualStateManager.GoToState(this, "Alive", true);
et
VisualStateManager.GoToState(this, "Dead", true);
Pour plus d'info sur le VisualStateManager, je vous conseille de visiter le blog de Christian Schormann.
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :