Storyboarded animations for visual states
Visual states are a technique whereby changes to the current value of properties of a control can result in changes to the UI and control template as defined in XAML. The visual state model provides current UI state feedback to the user as well as maintaining the design and logic separation that is used by XAML controls for a Windows Store app built for Windows using C++, C#, or Visual Basic. To implement the actual property changes for a visual state, you define a set of storyboarded animations in XAML. The animations you define for visual states use the same basic syntax as any storyboarded animation. However, there are some best practices for how the animations should be defined so that they're appropriate for a visual state. Animations for visual states should either be instantaneous (zero-duration) or fairly rapid, lasting less than a second. Also, it's important to design the set of animations used for visual states such that any state-related changes are changed back to an original or default value when that state is exited by the control.
Roadmap: How does this topic relate to others? See:
Whenever you can, use zero-duration animations to apply property changes that are part of a visual state. A zero-duration animation is guaranteed to be an independent animation, and thus has minimal impact on the performance of the UI thread and thus the responsiveness of the control and the app that uses it.
Occasionally a visual state animation uses a translate transform animation, and these don't look very good with zero duration. But try to keep the duration short (no more than a second or so) because users can't interact well with elements that are moving. With a non-zero duration, you no longer have the zero-duration exemption from being a dependent animation, so which property you're animating becomes an important factor. You might also consider designing these kinds of states with a VisualTransition with a duration but with the destination visual state animation being zero duration.
For more info on the implications of a dependent animation and what animations are considered dependent, see "Dependent and independent animations" section of Storyboarded animations.
For visual states, the apparent FillBehavior of a storyboarded animation is different than how they'd behave if you applied them directly to a UI element property. If a visual state is changed to another state, all the property changes applied by the previous visual state and its animations are canceled, even if the new visual state doesn't specifically apply a new animation to a property. (In contrast, a storyboarded animation directly targeting an app UI property has HoldEnd behavior; for more info, see Storyboarded animations.)
For many visual state changes, you only need to define a new visual state that's empty (has no animations and no Storyboard value). Your control logic can go to the empty visual state in order to deactivate the animations applied by other visual states in that VisualStateGroup. For example, many control templates have a state named "Normal" in the "CommonStates" VisualStateGroup, which has no Storyboard value. The control logic can go to the "Normal" state in order to cancel the other visual state animations in the same group such as "PointerOver", "Disabled" and so on.
Note When a control goes to an empty visual state, properties of a control use the original template values or other defaults, unless UI definition XAML has a value or the app code changes it at run time. More precisely, when the animation to the property value is disabled, the value is re-evaluated according to dependency property value precedence; for more info on this concept see Dependency properties overview.
Most controls that define visual states and that can be selected or invoked in some way have a primary visual group named "CommonStates". Within "CommonStates" you typically see these named states:
"Normal" is the state for when the control is not pressed, not disabled and doesn't have the pointer over its hit test area. "PointerOver" and "Pressed" are typically states that last a relatively short period of time and are tied to user action.
"Disabled" is used when the value of IsEnabled is false, or other properties like IsReadOnly declare that the control can't be used. These properties are typically set by app code. You app logic might disable controls for contexts where the user should use some other control instead but you choose not to hide the disabled control entirely.
In most cases if you are working with a visual state you are modifying an existing design, either from the template copy you started with or from the default template of the control class you are using as a base class. You can use these existing design behaviors as guidelines for what each of the "CommonStates" visual states should show to the user. If you still have questions about design for these visual states, or are writing a template that doesn't have much in common with other existing templates and need guidance, see Guidelines for visual feedback and Responding to user interaction. Touch and mouse input both generate pointer events. At the visual state level, these modes of interaction aren't usually differentiated.
One of the more important aspects of interactivity and state that a control should provide is a way for the user to tell when the control has focus in a UI. A focused control is the only control that can accept keyboard input so it's important for the user to know that a text area in the UI is focused for this purpose. Also, for accessibility reasons, it's important to identify the focus even for controls that don't use keyboard input as text input. For example, controls such as buttons should support an invoke mode so that a keyboard key (usually Space or Enter) can perform the same action that touching or clicking the button has. If done correctly, users can use the Tab key and other keys to interact with a UI without using touch or the mouse. For more info on why this is important, see Implementing keyboard accessibility.
A typical visual state uses a Rectangle that's initially not visible to act as the focus indicator (
Opacity="0"). The Rectangle is an immediate child of the template root, which is usually one of the panel classes. Make sure that the Rectangle uses a narrow Stroke, which only covers padding areas around the content of the control when it has focus. Otherwise a focused control might hide important content from the user. In a typical visual state design, the states for this are part of a VisualStateGroup named "FocusStates", and there are two states named "Focused" and "Unfocused".
In the "FocusStates" VisualStateGroup, you should define a separate empty visual state "PointerFocused". The focus indicator shouldn't appear if it's a pointer that directs focus to a control. The focus indicator is intended to show element-specific focus as controlled by a user using the tab sequence, or by initial programmatic focus when the app page is first loaded.
- Controls such as CheckBox define a "CheckStates" VisualStateGroup. In the default template, the named states in this group are: "Checked", "Unchecked", "Indeterminate".
- Controls that support initiating a drag-drop operation have a group named "DragStates" with quite a few states within. You won't see these in StandardStyles.xaml, but you'll see them if you edit a copy of the GridViewItem template, for example.
- Controls that support item selection have several selection-related states in the templates for their item types. The names of the groups and states vary per control. You can see these if you edit a copy of the template.
- The LayoutAwarePage template that you can use from Microsoft Visual Studio projects for apps defines visual states for each view state: "FullScreenLandscape", "Filled", "FullScreenPortrait", "Snapped".
Here's some example XAML of a typical set of visual states in a "CommonStates" VisualStateGroup. This is taken directly from the StandardStyles.xaml file that's included with most projects, and represents the states you might edit if you wanted to customize some of the AppBar buttons in your app. It's edited somewhat, with
... ellipsis shown because you don't need to see the whole file or every visual state to see the basic principles.
<Style x:Key="BackButtonStyle" TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid"> ... <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Visibility" > <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> ... </VisualStateManager.VisualStateGroups> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
- The "Disabled" state is applying a storyboarded animation to the Visibility property. The Visibility property takes an enumeration, so to animate it, you need to use a DiscreteObjectKeyFrame to set the Value to a particular enumeration constant of the Visibility enumeration. Applying a change to the Visibility of a composition element or sometimes the whole control is very common in visual state definitions.
- Note how the "Normal" state is just empty. The control logic can use this state to cancel the visual state named "Disabled" and restore the Visibility to its template-defined value, which is the default Visible.
Typical control logic uses various input events and status properties to determine which visual state from the various visual state groups should be active in a control at run time.
- Pointer-related visual states ("PointerOver" and "Pressed") can be changed by handling the PointerEntered and PointerExited events. Usually controls handle these events by overriding the built-in handlers OnPointerEntered and OnPointerExited.
- "Disabled" can be called by IsEnabledChanged or handlers for changes to other control-specific properties like IsReadOnly.
- Focus visual states can be changed by handling the GotFocus and LostFocus events. Usually controls handle these events by overriding the built-in handlers OnGotFocus and OnLostFocus. For OnGotFocus, control logic should check the FocusState to see whether it's the pointer that caused the focus change, and if so use the "PointerFocused" state rather than "Focused".
In addition to defining the various named visual states, a VisualStateGroup can also have a collection of VisualTransition elements. A VisualTransition defines an animation behavior that runs when a state change happens (when GoToState is called). You'll see a visual transition when the old and new state involved in the state change have property value differences, and there's a VisualTransition that's referencing the old/new named states as a From or To value.
For a VisualTransition you don't typically have a zero duration, because the purpose of a visual transition is to show a change over time as the visual states change.
Broadly speaking there are two ways to define a VisualTransition: by using a specific Storyboard that defines one or more properties where you apply a timed storyboarded animation, or by relying on the GeneratedDuration behavior.
A specific Storyboard uses basically the techniques described in this topic and the Storyboarded animations topic, except that you don't want to use zero-duration animations in this Storyboard . The properties you animate in the visual transition don't have to properties that changes between the visual states involved in the transition.
A GeneratedDuration behavior just has to have a non-zero value of GeneratedDuration. Then, the visual state system determines which properties are changing value between the old and new states, and generates an interpolated animation between the changed values that lasts the duration. The GeneratedDuration behavior only works for values that can be interpolated by animation: Double, Point, or Color values.
For a VisualTransition you define the visual states that the VisualTransition should transition between. From references the old state and To references the new state. If you have a value for From but not To, or vice versa, the unset value is interpreted as referencing any other state that's also in the same visual state group. A VisualTransition with neither From nor To does nothing.
You can use a different interpolation behavior than the default of a linear interpolation, by setting a value for GeneratedEasingFunction. For a list of the possible easing functions you can use and the mathematical function-over-time formulas that each represents, see Key-frame animations and easing function animations in (Windows Store apps using C#/VB/C++ and XAML).
Be careful with the GeneratedDuration behavior. Because it's not a zero-duration animation by definition, you might be accidentally creating a dependent animation on properties that are changing, and impacting the performance of your control or app.
VisualState animations run any time a state is entered, even when the control first appears. VisualTransition animations do not, there's no transition into the first state loaded, even if there's a transition that specifies the first state as its To.
Tools such as Visual Studio and Blend for Microsoft Visual Studio 2012 for Windows 8 supply an easy way to re-template a control by starting with a copy of the system's default template and placing it somewhere that's usable and modifiable by your app. Any visual states for a control are also included in that template, within a VisualStateManager.VisualStateGroups XAML node right under the root element of the template's content, in the main Template style setter. Whenever you are working with a template copy, make sure to reproduce all the named visual states and visual state groups that the template had to start with. If you fail to do this, the control might be missing important visual states that inform the user of interactions in the UI experience. The control logic will attempt to call GoToState to access expected named states that don't exist in your custom XAML template, and the VisualStateManager behavior is that the control will remain in the last visual state that loaded correctly. For example the control might get trapped in a "PointerOver" state even when the pointer is no longer over the control, if you didn't supply the "Normal" state that's typically called when the pointer exits.
For more info on modifying control templates, see Quickstart: Control templates.
Using visual states to help define current UI states isn't limited to control templating scenarios. You can use visual states any time that you want to apply a set of property changes that should only apply during a detectable condition. For example, you can use visual states for pages of your app in order to detect snapped and fill states and change layout properties in response. In particular, you can exclude certain elements from the snapped or filled view states and only display them when the app has the typical display area. The default app templates in Visual Studio often have visual states for view states already existing in the templated XAML, with state-switching logic in LayoutAwarePage code. Visual states for non-template scenarios still need some code to watch for state changes and invoke the correct visual state on the UI element that uses the states. This might involve handling events that are part of the app model, such as events on Window or activation handlers on Application.
- Responding to user interaction
- Storyboarded animations
- Roadmap for creating apps using C#, C++, or VB
- Dependency properties overview
- Key-frame animations and easing function animations
- Quickstart: Control templates
Build date: 3/5/2013