8.1. Animation Fundamentals
Animation involves changing some visible characteristic of a
user interface, such as its size, position, or color, over some time period.
You could do this the hard way, by creating a timer and modifying the user
interface's appearance on each timer tick. Indeed, this is how animation is
typically done in Win32 or Windows Forms. Fortunately, WPF takes care of these
low-level details. Animation, like many features of WPF, simply requires us to
declare what we would like done. The system takes care of doing it for us.
All of WPF's animation support boils down to changing one or
more properties over time. This means that there are some limitations on what
WPF's animation system can do for you. For example, the structure of the visual
tree remains the same throughout. An animation will not add and remove elements
for you (although it is possible for animation to set properties that will make
elements invisible). There is no way of providing a "before" and "after" scene,
or of getting WPF to interpolate between the two. This means there is no
automatic way of animating a transition from one layout to another in such a
way that the elements all slide from their start positions to their end
positions.
The key to knowing what animation can or cannot do is to
understand its property-focused natureit just changes whichever properties you
tell it to. When deciding how to animate a UI, ask yourself what you would
expect to see exactly halfway through the animation, and work out how the
properties would need to be set in order to capture that halfway point. If you
apply this thought process to animating from a horizontal to a vertical StackPanel,
it's obvious that there's a problem. You can't set a property on a StackPanel
to make it display something halfway between horizontal and vertical. And if
you can't, neither can the animation system! (If you wanted to achieve this
kind of effect, you would probably use a Canvas, as that allows
arbitrary placement of elements. You would need to animate each element's
position and size manually.)
Before we look at any of the parts of the animation framework in
detail, let's examine a simple example.
Example 8-1 shows the markup for a window containing a single red
ellipse. The Ellipse element's Height is set to 100, but it
does not declare a Width directly. Instead, the Width property
is determined by an animationthe ellipse will change its width over time.
Example 8-1. A simple animation
<Window Text="Simple Animation" Width="320" Height="150"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
<Window.Storyboards>
<SetterTimeline TargetName="myEllipse" Path="(Ellipse.Width)">
<DoubleAnimation From="10" To="300" Duration="0:0:5"
RepeatBehavior="Forever" />
</SetterTimeline>
</Window.Storyboards>
<Ellipse x:Name="myEllipse" Fill="Red" Height="100" />
</Window>
The animation is declared inside the Window.Storyboards
property. A storyboard is a collection of animations and is used to
coordinate multiple animations. When animations are defined in markup, they
always appear inside storyboards, even in simple examples like this one where
there is just one animation.
The animation in this example consists of two pieces, a SetterTimeline
and a DoubleAnimation . The SetterTimeline
determines what is to be animated through its TargetName property,
which refers to the ellipse's x:Name property. Its Path property
indicates that the ellipse's Width is to be animated.
 |
The Path property needs the name of both
the property to be animated and the class that defines the property. This is
because properties do not always have to be defined by the class they are
applied toyou might want to animate attached properties such as Canvas.Left.
For consistency, you are required always to specify both class and property,
even when the property is a member of the target object.
|
|
The DoubleAnimation nested inside the SetterTimeline
determines how the animated property is to be changed over time. The
significance of the "Double" in DoubleAnimation is that the property
being animated is of type Double, as opposed to Int32, Point,
Size, or some other type. Not all types are animated in the same way.
For example, Point is a two-dimensional value, meaning we may want
control over aspects of its animation that wouldn't make sense for a
one-dimensional type like Double. The Ellipse.Width property
we are animating here is of type Double, so we must use a DoubleAnimation.
Example 8-1 sets
the From property to 10, the To property to 300, and the Duration
property to 0:0:5. As you might guess, this means that the width will start at
the value 10 and will gradually change to 300 over the course of 5 seconds. The
RepeatBehavior property has been set to Forever, indicating
that once the animation reaches the end, it should go back to the start and
repeat indefinitely. Figure 8-1
shows how the ellipse appears at various stages during the animation.
 |
Make sure you specify all three parts of any
duration value in an animation. The value 2 would be interpreted as 2
hours! If you meant 2 seconds, you must use 0:0:2, meaning 0 hours, 0
minutes, and 2 seconds.
|
|
As we will see, there are a number of ways of choosing exactly
how the properties change, making it
straightforward to support curved motion and changes in speed, but these are
all just ways of getting WPF to set properties to the right value at the right
time.
8.1.1. Animatable Properties
The majority of properties that have an impact on an element's
appearance can be animated. There are just three requirements to be able to
animate a property: the property must be a dependency property
, a suitable animation type must be available, and the target element must
derive from FrameworkElement.
The animation system relies on the dependency-property system to
be able to update property values automatically.
Chapter 9 describes dependency properties in detail. The majority of
properties of WPF elements are dependency properties.
The second requirement, that the property's type must have a
corresponding animation type, refers to types such as DoubleAnimation or
PointAnimation . WPF supplies animation
types for the majority of types used by properties that affect appearance. The
main exceptions are enumeration types. For example, the Orientation type
used by StackPanel has no corresponding animation type. This makes
sense when you consider that this enumeration supports just two values, Horizontal
and Vertical. There is no way to represent some intermediate value
between these choices, so animation is not supported.
 |
You can write your own animation types. This can be
useful if you write a control that has properties of some custom type that you
would like to animate. Technically, there is nothing stopping you writing an
animation type for system types that do not support animation. For example, you
could in theory write an OrientationAnimation. However, it would be of
limited use, because at any given moment during the animation, it would be
required to set the property to one of the two supported values: Horizontal
or Vertical. There is no way of animating smoothly between these two
values, so the best you could do is flip from one to the other partway through
an animation.
|
|
The final requirement listed above is that the target element
for animation must be a FrameworkElement. This is usually not a
problem, because all WPF user-interface elements derive from this class.
However, there will sometimes be quantities you may wish to animate that are
not in fact properties of FrameworkElements, but which are nested
properties of properties. For example, the ellipse in
Example 8-1 is red, but we might want to animate this color. The Fill
property's type is Brush, and the XAML compiler interprets the value
of Red as shorthand for a SolidColorBrush property. (Brushes
are discussed in detail in
Chapter 7. The way that XAML converts strings to objects is discussed
in Appendix A.)
Example 8-2 shows the markup for the full versionthis is exactly
equivalent to the one-line Ellipse declaration in
Example 8-1.
Example 8-2. Ellipse with explicit SolidColorBrush
<Ellipse x:Name="myEllipse" Height="100">
<Ellipse.Fill>
<SolidColorBrush Color="Red" />
</Ellipse.Fill>
</Ellipse>
This fully expanded version makes it clear that to change the
ellipse's color over time, we need to animate the SolidColorBrush.Color
property. But there's a problem. SolidColorBrush is not a FrameworkElement,
because brushes are not a part of the user-interface tree. Brushes are very
lightweight objects that describe how elements look, rather than being visible
elements in their own right. You cannot assign an x:Name to a brush,
and it cannot be the direct target of an animation.
This may seem like a rather severe restriction. Fortunately, a
solution exists. An animation can target nested propertiesthe Path of
a SetterTimeline can drill into sub-objects inside a property, and we
can use that to animate properties of brushes and other similar lightweight
types.
Example 8-3 shows
how to animate the color of the ellipse.
Example 8-3. Animating nested properties
...
<Window.Storyboards>
<SetterTimeline TargetName="myEllipse"
Path="(Ellipse.Fill).(SolidColorBrush.Color)">
<ColorAnimation
Duration="0:0:7" From="Red" To="Purple"
RepeatBehavior="Forever" AutoReverse="True" />
</SetterTimeline>
</Window.Storyboards>
...
The animation needs a FrameworkElement as its target,
so its TargetName refers to the ellipse again. The SetterTimeline.Path
property first identifies the Ellipse.Fill property and then indicates
that it wants to drill into that property, which is a SolidColorBrush,
and set the nested Color property. The ColorAnimation then
specifies that the color should fade between red and purple every seven
seconds.
 |
If you are using the low-level geometry types
described in Chapter 7
to build drawings, you will need to use the technique shown in
Example 8-3, because Geometry does not derive from FrameworkElement.
You can animate geometries inside the Data property of a Path
by making the Path element the animation target and using the Path
property of the SetterTimeline to specify properties on the geometry
nested inside the Path. The same technique is also used to animate 3-D
primitives.
|
|
SetterTimeline and the various animation types are all
examples of timelines. Timelines are fundamental to animation, so we will now
look at them in detail.
 |