Visual States are the way Silverlight communicates basic state information about an object. Is a button pressed? Is a mouse over a button? Is a button normal? The available states for an object are indicated via decoration with the TemplateVisualStateAttribute (in System.Windows). For example, the Button class in Silverlight looks like this:
[TemplateVisualState(Name="Unfocused", GroupName="FocusStates"),
TemplateVisualState(Name="MouseOver", GroupName="CommonStates"),
TemplateVisualState(Name="Normal", GroupName="CommonStates"),
TemplateVisualState(Name="Pressed", GroupName="CommonStates"),
TemplateVisualState(Name="Disabled", GroupName="CommonStates"),
TemplateVisualState(Name="Focused", GroupName="FocusStates")]
public class Button : ButtonBase
{
// Methods
public Button();
internal override void ChangeVisualState(bool useTransitions);
public override void OnApplyTemplate();
protected override void OnClick();
protected override AutomationPeer OnCreateAutomationPeer();
}
You might notice from this code that the various states also have group names. If you look at the states it makes sense why we would want to group them. States like Unfocused and Focused are related. States like Mouse Over, Normal, Pressed, and Disabled are all related to how a button looks with various interactions. Silverlight only allows one state from any group of an object to be active at once. This also makes sense. A button cannot be Focused and Unfocused, or Pressed and Normal, but a button can be both Pressed and Focused. I like to think of each group like an enumeration, and the states of each group as the values that enumeration can hold.
When the button switches to a new state, it does so by calling VisualStateManager.GoToState(...). Of course there needs to be some logic to fire this switch, and this is ultimately driven by built-in events like MouseEnter, MouseDown, etc. To switch to Focused, the code might look like:
VisualStateManager.GoToState(this, "Focused", true);
To consume visual states one uses something called a VisualStateGroup class which contains a VisualState objects, one each corresponding to a visual state decorated on the class. This object is attached to the object's template. Here is some sample XAML for the button class:
<ControlTemplate TargetType="Button"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
<Grid >
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualStateGroup.Transitions>
<!--Take one half second to trasition to the MouseOver state.-->
<vsm:VisualTransition To="MouseOver"
GeneratedDuration="0:0:0.5"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Normal" />
<!--Change the SolidColorBrush, ButtonBrush, to red when the
mouse is over the button.-->
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonBrush"
Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Grid.Background>
<SolidColorBrush x:Name="ButtonBrush" Color="Green"/>
</Grid.Background>
</Grid>
This markup generates a VisualStateGroup named to correspond to a group on the object. It contains visual states, each named to correspond to an existing state on the object. The VisualStateGroup can also contain transition objects to control the transition to any given state. The VisualState itself doesn't do anything but start a Storyboard animation. From that animation you target objects and properties as you would any other story board. The pieces all just fall into place!
Many built-in object contain a number of visual states that you can utilize with control templates to change look and feel, but you can also create your own visual states on custom objects, and consume those states in your own custom code. While this system is not as flexible as the event triggers built into WPF, it does allow for some interesting capabilities.