Why Visual States Exist
Visual states let a control's ControlTemplate declare named appearance variations — like Normal, MouseOver, Pressed, or Disabled — directly in XAML instead of scattering Storyboard triggers across code-behind. VisualStateManager.VisualStateGroups is attached to the template's root element and groups related, mutually exclusive states together, such as a CommonStates group holding Normal/MouseOver/Pressed/Disabled.
Cricket analogy: VisualStateGroups are like a match officials' rulebook naming discrete states — Play, Drinks Break, Rain Delay — declared once in the regulations rather than umpires improvising ad hoc calls, so every ground follows the same named states.
VisualStateGroups and Mutually Exclusive States
Within a single VisualStateGroup, only one VisualState can be active at a time — a button can't be simultaneously Normal and Pressed — but a control can have several independent groups active simultaneously, such as CommonStates and FocusStates changing different visual properties at once. Each VisualState contains a Storyboard that animates specific properties (like Background or Opacity) on named elements inside the template.
Cricket analogy: A single VisualStateGroup enforces exclusivity like an umpire's decision — a batsman is either Out or Not Out, never both — while a separate independent group like weather conditions (Sunny/Overcast) can change at the same time without conflicting.
Transitioning with GoToState
Control authors call VisualStateManager.GoToState(control, "MouseOver", true) from code, typically inside overridden OnMouseEnter/OnMouseLeave handlers, to request a transition into a named state; the boolean parameter controls whether the transition should animate or snap instantly. VisualTransition elements let you customize the animation duration and easing used when moving from one specific state to another, rather than relying on a single default transition for every state change.
Cricket analogy: Calling GoToState from OnMouseEnter is like a twelfth man sprinting onto the field the instant a fielder signals injury — the transition is triggered by a specific event, and how fast it happens can be tuned like a VisualTransition's duration.
Visual States vs. Triggers
Compared to older Trigger/DataTrigger-based styling, visual states centralize all of a control's appearance logic into one place per state, making a custom ControlTemplate easier to reason about, and they compose better with the built-in states that ItemsControl-derived and ButtonBase-derived controls already expect. However, because states are typically driven from a control's own code (not purely from data bindings), fully custom controls need a corresponding C# partial class that calls GoToState at the right lifecycle moments.
Cricket analogy: Centralizing appearance logic per visual state is like keeping every rule for a rain-delay procedure in one page of the playing conditions document, rather than scattering related clauses for the same delay across several separate rulebook sections.
<ControlTemplate TargetType="Button">
<Border x:Name="Bg" Background="#3A6EA5" CornerRadius="4">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bg"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="#4C8BC9" Duration="0:0:0.15"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="Bg"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="#2C5580" Duration="0:0:0.05"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Bg" Storyboard.TargetProperty="Opacity" To="0.4" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>Always include an empty Normal VisualState (even with no Storyboard) in every VisualStateGroup — it gives GoToState a defined baseline to animate back to when leaving MouseOver, Pressed, or Disabled, instead of leaving the control stuck at its last animated values.
A Storyboard inside a VisualState that targets a name via Storyboard.TargetName silently does nothing if no element with that x:Name exists in the current template — there's no compile error, the state transition simply has no visible effect, which makes typos in TargetName a common source of 'my hover state isn't working' bugs.
- VisualStateManager.VisualStateGroups on a template's root element groups related, mutually exclusive states.
- Only one VisualState within a given VisualStateGroup can be active at a time.
- Multiple independent VisualStateGroups (e.g., CommonStates and FocusStates) can be active simultaneously.
- Each VisualState contains a Storyboard animating specific properties on named template elements.
- VisualStateManager.GoToState(control, stateName, useTransitions) requests a transition into a named state.
- VisualTransition elements customize the duration/easing used between specific state pairs.
- Visual states centralize a control's appearance logic per state instead of scattering trigger-based rules.
Practice what you learned
1. Where is VisualStateManager.VisualStateGroups typically declared?
2. Can two VisualStates within the SAME VisualStateGroup be active at once?
3. What does the boolean parameter to VisualStateManager.GoToState control?
4. What happens if a VisualState's Storyboard targets a name that doesn't exist in the template?
5. What is a VisualTransition used for?
Was this page helpful?
You May Also Like
Content Controls in XAML
How ContentControl and its subclasses (Button, Label, Border, ScrollViewer) hold a single logical child via the Content property, and how ContentPresenter and ContentTemplate render it.
ItemsControl and Templates
How ItemsControl and its subclasses (ListBox, ComboBox, ListView) render a collection via ItemsSource, ItemTemplate, and ItemsPanelTemplate.
Custom Markup Extensions
How to author a custom MarkupExtension by overriding ProvideValue, and when to reach for one instead of a converter or plain binding.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics