This article is compatible with the latest version of Silverlight.
Introduction
We’ve introduced you to the basics of Custom Controls in our previous article. Now we will give another example this time involving the Visual States Manager. We will create a templatable ContentControl that uses animations on its Content and the States & Parts model proposed by Microsoft.
Dowload source code
Parts & States Model
The idea behind the States & Parts is to separate better the logic from the visual appearance and thus to allow better flexibility for both.
This is a pattern proposed by Microsoft that is not obligatory but if you want to be able to skin(use templates) your controls in Expression Blend (which will be supported in the next versions) it is good to be aware of it. It defines a contract of what are the common parts of the logic and the visuals allowing separate development, still keeping synchronization. how to separate logic and visuals or more likely what are their common parts. So probably this is where it comes the name from.
Parts
You define the different parts of the control before the declaration of the control class using the following syntax
[TemplatePart( Name = "PartName", Type = typeof( PartType ) )]
[TemplateVisualState( Name = "VisualStateName", GroupName = "StatesGroupName" )]
TemplatePart describe the name and type (class) of the part that should be defined in generic.xaml. For example there could be base,tick and text parts if we want to create a a checkbox. In our example we have only one part which is the content that we put in our control. Later we will animate this part and its content consiquently.
[TemplatePart( Name = "ContentPresenter", Type = typeof( ContentPresenter ) )]
The second type of declaration is related with the VisualStateManager.
States
States are introduced to provide an easier way of defining the appearance of a control. So each state represents a different appearance. To reduce the number of definitions, states are divided into groups. Different groups are orthogonal or to put it in other words states in one group are independent of the states in other (this looks like implementation of Bridge pattern for those interested) . To keep it simple in our example we have only one group and three states.
[TemplateVisualState( Name = "Normal", GroupName = "CommonStates" )]
[TemplateVisualState( Name = "MouseOver", GroupName = "CommonStates" )]
[TemplateVisualState( Name = "Pressed", GroupName = "CommonStates" )]
If you have two or more groups transitions will happen independently and sometimes simultaneously (consider we have a "Click" and "Focused" states animations on a button click event).
VisualStatesManager
VisualStaesManager is a class taking care of the transitions between different states.It has one essential part that is a list of VisualStateGroup items. VisualStateGroup defines its name, states and transitions. A state itself has a name and a Storyboard defining the animation that executes during that state. Transition property is not always necessary. If you leave it out, transition between the two states will be instant i.e. storyboard of the state will be started immediately. If you define transitions you can explicitly indicate the duration of a transition. As a result VSM will generate automatically Storyboard animations that make linear interpolation of the properties. This is the whole definition of the VSM in our example.
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
...
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Normal">
<Storyboard>
...
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed">
<Storyboard>
...
</Storyboard>
</vsm:VisualState>
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition GeneratedDuration="0:0:.5"/>
<vsm:VisualTransition From="Normal" To="MouseOver" GeneratedDuration="0:0:1"/>
</vsm:VisualStateGroup.Transitions>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
You can set either, both or neither of the "From" and "To" properties resulting in the appropriate behavior of the transitions.
It is important to include VisualStateManager somewhere in xaml like this
<SomeParent ...
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
...
/>
There is a problem in the Visual Studio with the visualization in the design view and with the appearance of errors which appear to be faulty. You can run your project despite the errors.
So far we explained how to define the properties of VisualStateManager and now we just need to start using it in our code. We simply call the static
VisualStateManager.GoToState( Control control, string "StateName", bool useTransitions );
to change the visual state of the control. Microsoft recommends you to define a dedicated function to choose the appropriate state. Here is what we use in our example.
private void GoToState( bool useTransitions )
{
if ( isPressed )
{
VisualStateManager.GoToState( this, "Pressed", useTransitions );
}
else if ( isMouseOver )
{
VisualStateManager.GoToState( this, "MouseOver", useTransitions );
}
else
{
VisualStateManager.GoToState( this, "Normal", useTransitions );
}
}
Changing properties of VSM Storyboard during runtime
While we were experimenting with the VSM we tried to change some properties of the animations targeted by the VSM during runtime. This led to some errors we couldn't get through so we made an inquiry to the forum at silverlight.net. It turned out to have something with the Storyboards in VisualStateManager having their own name scopes. So instead of trying to access the Storyboards in code directly using their names you should get access by performing the following query
var query = from state in
( from stateGroup in VisualStateManager.GetVisualStateGroups( this.LayoutRoot )
where stateGroup.Name == "CommonStates"
select stateGroup ).FirstOrDefault().States
where state.Name == "MouseOver"
select state;
Storyboard sb = ( Storyboard )query.First().Storyboard;
supposing the Storyboard you want to access is in the VSM of LayoutRoot.
Conclusion
The States and Parts Model is a nice way to decouple the process of development and the process of (visual) design. This makes the whole process of creating application faster and more flexible. The Visual State Manager makes it easier to support states which is a pretty common scenario with controls and is quite useful. With future support by Expression Blend for templates the resources and possibilities of Silverlight start reaching the level of one powerful tool for creating RIA.
References
States and Parts Model introduction in 4 parts by Karen Corby:
http://scorbs.com/2008/06/11/parts-states-model-with-visualstatemanager-part-1-of/
MSDN Links
ContentControl:
http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol(VS.95).aspx
ContentPresenter
http://msdn.microsoft.com/en-us/library/system.windows.controls.contentpresenter(VS.95).aspx