Поделиться через


Storyboarded animations for visual states (XAML)

Visual states are a technique whereby changes to the state of a control will change the UI, by loading XAML control templates for the current state. 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. To change properties for a visual state, you define a set of storyboarded animations in XAML. The animations for visual states use the same XAML 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 value when that state no longer applies.

Roadmap: How does this topic relate to others? See:

Zero-duration animations

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. This affects the responsiveness of the control and the app that uses it.

Occasionally a visual state animation requires a translate transform animation, and these don't look good with zero duration. But try to keep the duration short (no more than a second or so) because users can't interact easily with elements that are moving. For a non-zero duration, you may no longer get 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.

Creating visual states that restore the original values

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 define a new visual state that's empty (it has no animations and no Storyboard value). Your control logic can invoke the empty visual state in order to deactivate the animations applied by other visual states in the same 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"
  • "PointerOver"
  • "Pressed"
  • "Disabled"

"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.

Indicating visual focus

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.

More visual states

  • 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'll see these states in the starting XAML if you edit a copy of the GridViewItem control 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.
  • Some of the Windows 8 era Microsoft Visual Studio projects for apps define visual states for display-area view states. The Windows 8.1 templates don't do this anymore.

Some example XAML

Here's some example XAML of a typical set of visual states in a "CommonStates" VisualStateGroup. This is taken directly from the Windows Runtime default templates for controls and other UI elements. You might edit a copy of the default template XAML if you want to customize some of the AppBar buttons in your app. As shown here the XAML is edited somewhat from the full template, with ... ellipsis shown because you don't need to see the whole file or every visual state to understand 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 Value="Collapsed" KeyTime="0"/>
                  </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 empty. The control logic can load this state to cancel the visual state named "Disabled" and restore the Visibility to its template-defined value, which is the default Visible.

Control logic for using visual states

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 (for example "PointerOver" and "Pressed") can be changed by handling input events (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".

VisualTransition

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.

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.

Modifying template copies

Tools such as Visual Studio supply an easy way to re-template a control by starting with a copy of a segment of the Windows Runtime default template XAML and placing it somewhere that's usable and modifiable by your app and project. 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.

Don't use visual states from different groups to animate the same property

Most visual state models have more than one visual state group, where each group is responsible for representing state for a set of conditions that are exclusive within that group. For example, a control might have a group that responds to pointer events, a set that responds to current keyboard focus, and a state for text input. As such a control is really using multiple states at a time. If there are states from different groups modifying the same property as a visual state action, what value is used becomes indeterminate, because it now depends on which state was entered last. Don't do this. It may be necessary to introduce closely related elements into your control template to make sure that different visual state groups are using different elements when they animate properties that affect the visual state of the control.

Using the animation library in visual states

The animation library for the Windows Runtime includes several theme animations. Theme animations are animations to one or more properties of predefined Windows Runtime XAML UI types. Some of the Windows Runtime XAML controls include theme animations within their default visual states. These are found in two possible places:

  • As one of the animations in the VisualState.Storyboard for one or more visual states of the control. In this location, the theme animation runs when that visual state is used (when the control logic calls GoToState referencing that state).
  • As one of the animations in the VisualTransition.Storyboard, within VisualStateGroup.Transitions. In this location, the theme animation runs when the visual state manager executes the transition between the two relevant named states.

If you're using the typical technique of starting with a XAML copy of the default control template that you obtained with Edit Template/Edit a Copy in Microsoft Visual Studio, you might see theme animations in your starting copy. You generally shouldn't remove theme animations from a control's template. The theme animations are there deliberately, not just to provide look and feel for that particular XAML control, but to reinforce a Windows user experience overall.

To use a theme animation, you must target that animation at a particular part within the template. To do this you set the TargetName attribute of the theme animation, using a string that is the x:Name of one of the template parts defined elsewhere in the template (these are the parts that define the starting template). Theme animations target specific properties inherently, and sometimes target more than one related property behind the scenes. Because it's not immediately obvious which properties a theme animation is targeting, it's also not obvious what parts you should be targeting by TargetName such that the target and the properties being animated line up.

Important  Don't use Storyboard.TargetName or Storyboard.TargetProperty when you're targeting a theme animation. That targeting model clashes with the nonattached TargetName attribute and with the inherent nature of property-targeting by theme animations.

 

When a control's default templates or styles use a particular theme animation, this is listed in the "Animating ..." topics that document how and when to use the specific library animations. For example if you look at Animating pointer actions, you'll see a section "Pointer animations in default Windows Runtime control behavior" where it's noted that ListViewItem and GridViewItem are including a PointerDownThemeAnimation in one of their visual states of the default template. You might also be able to see this noted in the style and template documentation for controls. For example, ListViewItem styles and templates shows you exactly where the PointerDownThemeAnimation exists in the "Default style" section that shows the complete XAML.

The theme animations are generally not zero-duration, but they are short durations, typically less than 2 seconds. They're carefully constructed such that they're targeting properties that don't result in dependent animations.

Most of the theme animations don't have properties other than TargetName and what they inherit from Timeline. But some of the theme animations do have additional properties. For example, PopInThemeAnimation has a FromHorizontalOffset property that modifies where the animation starts positionally. Modify these with caution because they're using defaults that present a cohesive Windows user experience, and changing the behavior radically can be just as detrimental as removing the theme animation entirely.

As stated previously, some controls have theme animations present by default in their visual states or visual transitions. But in some cases, the properties of a library animation that a control uses for its visual states or style can be styled without having to completely replace the control template. This is enabled by changing the TemplateSettings property value on such a control. Here's a list of controls where you can modify the theme animations outside of templates, referencing the property that supports it:

There's another type of animation in the animation library called a theme transition. They're used for *Transitions properties on specific controls, or perhaps the UIElement.Transitions property. Those properties aren't within the visual states, but theme transitions are relevant for control styling in general because you do typically set *Transitions properties in the default (implicit key) Style for a Control, or perhaps for other UI elements like a TextBlock. Some of the TemplateSettings listed previously affect the theme transitions. For more info on how to use theme transitions, see Animating your UI or Quickstart: Animating your UI using library animations.

Visual states for scenarios other than 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 app or window states and change layout properties in response. Visual states for non-template scenarios 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, system device events that detect display orientation changes or resolution changes, or activation handlers on Application. Some of these concepts overlap with the application model and how your app might keep track of view states; for more info, see Quickstart: Designing apps for different window sizes.

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

VisualStateManager

VisualState

VisualTransition

Storyboard

Storyboard.TargetProperty