In this article I want to illustrate interesting aspect of Silverlight visual states and how they can be used in controls.
It is not a secret that one of the key features in Silverlight controls is an ability to declare
different states for a control and specify transitions between them.
Each transition between states can take time for animation and often it is necessary to know when transition is really finished.
Let us suppose that we've spent a couple of hours and prepared simple button in Blend.
Something that looks like the following picture ;-)

The button above consists from 3 parts: LayoutRoot (grid with Width and Height bound to the "Width" and "Height properties of control), Host (button background with color property bound to the "Background" property of control) and Content (green arrow).
You can see how it works here:
Here is the result of such efforts in xaml code, which specifies simple style for the button. The following xaml shows visual state manager and visual states.
<vsm:VisualStateManager.VisualStateGroups >
<vsm:VisualStateGroup
x:Name="CommonStates" >
<vsm:VisualStateGroup.Transitions >
<vsm:VisualTransition
GeneratedDuration="00:00:00.4000000"
To="MouseOver" />
<vsm:VisualTransition
GeneratedDuration="00:00:00.4000000"
To="Play" />
<vsm:VisualTransition
GeneratedDuration="00:00:00.4000000" />
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState
x:Name="Normal" >
<Storyboard />
</vsm:VisualState>
<vsm:VisualState
x:Name="MouseOver" >
<Storyboard >
<DoubleAnimationUsingKeyFrames
BeginTime="00:00:00"
Duration="00:00:00.4000000"
Storyboard.TargetName="Host"
Storyboard.TargetProperty="(UIElement.Opacity)" >
<SplineDoubleKeyFrame
KeyTime="00:00:00"
Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState
x:Name="Play" >
<Storyboard >
<DoubleAnimationUsingKeyFrames
BeginTime="00:00:00"
Duration="00:00:00.4000000"
Storyboard.TargetName="Host"
Storyboard.TargetProperty="(UIElement.Opacity)" >
<SplineDoubleKeyFrame
KeyTime="00:00:00"
Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
Now we need to add behaviour logic to this button. It should have 2 methods: Start and Stop.
When mouse is moved over the button the Host should change its opacity. When Stop method is called or button is simply clicked the Content should start rotation - play state of the button.
So, from the point of implementation this can be done in the following way. As you can see from the picture, the Button contains 3 states: "Normal", "MouseOver" and "Play". When mouse pointer is moved over the control we should start transition to the MouseOver state and when the button is clicked we should start transition to the Play state and only after that start the rotation of content.
Please note: "only after that" start the rotation of the content ;) The problem is that transitions between the states can take a time. And if you start rotation of the content immediately after the button is clicked - it will occur in parallel mode with transition and trust me it will look really ugly. So to avoid this we definitely should start the rotation after the transition is finished.
Silverlight has easy way to do exactly this. We can use visual state manager and visual states. Xaml code contains VisualStateManager which declares VisualStates and Transitions. So implement our task we simply need to get access to the VisualStateGroups in the OnApplyTemplate method override and handle several events. Lets take a look how the code would look like.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
FrameworkElement root = GetTemplateChild("LayoutRoot") as FrameworkElement;
.........
commonStatesGroup = GetVisualStateGroup(root, "CommonStates");
HookVisualStateGroupEvents(commonStatesGroup);
}
VisualStateGroup GetVisualStateGroup(FrameworkElement root, string groupName)
{
if (root == null)
return null;
IList groups = VisualStateManager.GetVisualStateGroups(root);
if (groups == null)
return null;
foreach (VisualStateGroup gr in groups)
if (gr != null && gr.Name == groupName)
return gr;
return null;
}
void HookVisualStateGroupEvents(VisualStateGroup group)
{
if (group == null)
return;
group.CurrentStateChanged += group_CurrentStateChanged;
}
void group_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
if (e.NewState != null && e.NewState.Name == "Play")
OnStarted();
}
Now let's see what is done in the above code.
First we get access to the "CommonStates" visual group in the OnApplyTemplate method and then simply subscribe to CurrentStateChanged event.
VisualStateChangedEventArgs in the handler gives us only 3 properties: Control, OldState and NewState. However this is all we need in this handler. ;-)
When our event handler is called we can say that control was animated and we know which states animation was done for.
From this point we know that state of the control was changed and we can start content rotation.
Source
I hope this sample will be useful.