|This chapter covers|
The illuminating graphical capabilities described in chapter 8 laid the groundwork for creating valuable illustrations. An illustration is generally composed of a variety of shape and text elements. With Silverlight, you can bring these elements, and others, to life with the help of the built-in animation functionality.
When used practically, animations can be used to dramatically improve a user’s experience. This experience may demand dynamic data representations such as the speedometer in your car. Maybe you’re looking for a way to reassure your users of their actions. Or, the experience needs to be more visually appealing. Any way you slice it, animations can be used to add a significant amount of value. To illustrate this value, imagine meeting someone for the first time. Your initial conversation may go something like the following:
|You:||Hi, I’m <enter your name>. How are you?|
|Person:||Hey <enter your name>, I am Person. I am doing well; thank you for asking. How are you doing?|
|You:||I’m fair to middling. I can’t believe the game last night. Did you see that play at the end!?|
|Person:||[does not answer, has a blank stare…]|
|Person:||[does not answer, has a blank stare…]|
Granted, this scenario is unlikely to occur, but you can imagine your confusion if someone didn’t respond to your requests. Similarly, you’ve probably witnessed a web page where this scenario seemingly occurs. Users may click a link on a web page with little acknowledgment that their action has been noticed. As users may anxiously tap their toes or fingers, waiting for some kind of acknowledgment, their confusion or frustration grows. Eventually, their boiling points will be reached, often leading to the link being clicked again. This second click may lead to undesired effects such as adding an additional item to a shopping cart.
This scenario paints the backdrop for how animations can remove stress from users’ experiences. If an application doesn’t adequately respond to users’ requests, they’ll eventually become confused or frustrated. From your users’ perspectives, it may be difficult to determine if an application is still functioning. With animations, you can easily keep your users in the loop and inform them their request has been received.
Silverlight enables you to deliver animations through the System.Windows.Media.Animation namespace. Throughout this chapter, you’ll see how this package enables you to take advantage of a powerful technique known as key framing. In addition, you’ll see how animations are controlled and organized via another process known as storyboarding. Before we address these exciting topics, let’s look at the basics of animation.
An animation within Silverlight boils down to changing a single visual property over some period of time. Without the concept of time, an animation would be a static graphic, and there would be no need for this chapter. By gradually changing a visual property over the course of a time period, you can deliver dynamic effects. One such effect is shown in figure 9.1.
This figure shows the relationship between the Opacity property of an Image and the duration of an animation. As this animation progresses over the course of a single second, the Opacity value gradually increases. As the Opacity value increases, the Image gradually becomes more and more opaque. You create this dramatic animation by using the code in snippet 9.1.
This snippet shows the XAML responsible for fading an image into view. A lot of new elements are presented within this small example, and each one serves a distinct and vital purpose. For this reason, let’s examine each of these items in great detail. But first, to gain an understanding of how these elements relate to one another, here’s a quick overview of the items seen in snippet 9.1:
The EventTrigger element initiates an action when the Image is loaded. This action is represented as the BeginStoryboard element.
The Storyboard object is responsible for organizing and controlling the animations defined within it. Because of the BeginStoryboard action, this Storyboard is automatically started when the EventTrigger is fired.
The DoubleAnimation element specifies that you’re going to animate a double-precision value. There are other animation types that we’ll cover in a moment. But more importantly, the value to animate is referenced with help from the Storyboard.TargetProperty and Storyboard.TargetName properties.
As this outline demonstrates, each element serves a specific purpose. These elements work together to allow you to create lively animations. These animations ultimately revolve around time. Time is probably best represented as a line such as the one shown in figure 9.1. This timeline demonstrates how central the concept of time is to an animation.
At its base, every animation represents a Timeline object. This object is defined within the System.Windows.Media.Animation namespace and is used to represent a period of time. During this period of time, you have the opportunity to change the value assigned to a visual property. To specify which property value should be changed, you answer the following simple questions:
- What type of property are you animating?
- Where are you starting from, and where are you going?
- How long should the animation run?
Although these questions sound fairly basic, there are a significant number of details surrounding each one. For this reason, we’ll cover each question in detail, beginning with the first question.
To create an animation, you first select a single visual attribute of a single element. This item is guaranteed to have a data type associated with it. This data type will serve as the guiding light throughout the animation process. Ultimately, it’s what will decide the type of animation that should be used. Imagine having a basic Ellipse that you want to animate. The XAML for this sample is shown in snippet 9.2.
This snippet shows an Ellipse named myEllipse . This Ellipse will be used in the remainder of this section to describe animating properties. Silverlight provides three types of animations to assist you in creating dramatic visual effects. These three types differ in regard to the type of property being animate. Silverlight has the ability to animate double, Point, and Color values via the DoubleAnimation, PointAnimation, and ColorAnimation types. We’ll begin by discussing the most useful type, the DoubleAnimation.
A DoubleAnimation enables you to animate a single property value from one double-precision floating-point value to another. This is probably the most widely used type of animation. To illustrate a DoubleAnimation, snippet 9.3 shows how you could fade out the Ellipse defined in snippet 9.2 over the course of 1 second.
Storyboard.TargetProperty="Opacity" From="1" To="0"
As this snippet illustrates, delivering a fade effect is incredibly simple. The DoubleAnimation element prepares Silverlight to generate double-precision values between the From and To values. As you can imagine, this opens the doors to tons of valuable animation scenarios, but not every opened door should necessarily be entered.
Attempting to animate the FontSize property of a TextBlock can be a resource-consuming task. Even though this property represents a double-precision value, animating it can quickly lead to poorly performing applications because the text will be smoothed on each and every frame—an expensive process. For this reason, if you need to animate your text, you may want to consider converting your TextBlock into a Path and using a ScaleTransform.
Regardless, the DoubleAnimation is still applicable in a variety of scenarios, the creation of fades, moving elements around a Panel, and performing transformations, among other things. However useful the DoubleAnimation is though, there still may be situations where you need to animate Point -related values.
The PointAnimation type enables you to animate from one pair of x-and-y coordinates to another. As the name implies, this type of animation enables you to animate any property that represents a System.Windows.Point. And although this type isn’t as widely used throughout the Silverlight APIs as the double type, it still has its place. For instance, you may need to animate the origin of a transformation or dynamically change the presentation of a brush. Regardless of the need, it’s nice to know that you can rely on the PointAnimation, which is illustrated in snippet 9.4.
From="100,100" To="100,300" />
The animation in this snippet changes the origin of any transforms applied to the Ellipse in snippet 9.2. Generally, a PointAnimation will only be used in association with transforms and the Geometry elements mentioned in chapter 8. But, for a more subtle animation, you may consider using a ColorAnimation.
A ColorAnimation empowers you to create smooth transitions from one color to another. These transitions can be performed between any two System.Windows.Media.Color property values. For this reason, this type of animation is used primarily with a brush as shown in snippet 9.5.
Duration="00:00:01" From="Yellow" To="Red" />
This snippet shows an assumed Ellipse shifting from "Yellow" to "Red" over the course of one second. This animation, along with the others mentioned, shows how easy it is to animate a property. Up to this point, we’ve only focused on animation related to a property type. In reality you also need to know how to specify the exact property you’re animating.
Each of the animation types that we’ve discussed exposes two attached properties that specify the target of an animation. Appropriately, these attributes are called Storyboard.TargetProperty and Storyboard.TargetName. These two properties work in coordination to determine which property of a specific element will be animated. This is a very simplified description of these properties; a more detailed definition will be provided in section 9.3.2. For now, let’s turn our focus to the second question in our animation journey.
As figure 9.1 illustrated, an animation usually has a beginning and an end. The end of an animation can be specified using one of two properties. We’ll discuss each of these properties in detail later in this section. Before we can discuss the end of an animation, we should first discuss the beginning of an animation.
There is a saying that you can’t know where you’re going until you know where you’ve been. In regard to animation, this phrase should be changed to you can’t know where you’re going unless you know where you’re from. To identify where an animation is coming from, you rely on the aptly named From property.
The From property is accessible from all the animation types that we’ve discussed. This value determines where an animation will begin. Snippet 9.6 shows the From property in action to help jump start our discussion.
From="0" To="1" Duration="0:0:1" />
This snippet is preparing to animate the Opacity property of an assumed Image. The Opacity property of this Image is initially set to "0" when the animation starts. This is determined by the value provided within the From property. Once the animation begins, the Opacity value gradually increases over the course of one second to the value of 1.
Note that this value is compatible with the animation type. The "0" may look like an integer, but at runtime, it’s automatically converted into a double-precision value. If you’d attempted to set the From property value to "Yellow", an exception would have been thrown because "Yellow" isn’t a valid double-precision value. Alternatively, you can skip this potential problem altogether by not defining a From property value; the From property is an optional attribute.
If a From value isn’t provided, the animation will automatically decide where to start from. To decide where to begin, the animation will examine the target specified by the Storyboard.TargetName and Storyboard.TargetProperty attributes. Once these are examined, the From property will be set to the current property value associated with the target. Snippet 9.7 may better explain this.
Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1" />
<Image x:Name="myImage" Source="image1.png" Opacity=".25" />
When the animation in this snippet begins, it automatically determines that the Opacity value within the animation should begin at .25. This is the current value of the Opacity property, which is defined as the target. This approach can help create smoother, more fluid animations. On the other hand, explicitly stating the From value can have unexpected effects on your animations.
Explicitly setting the From value can cause your animations to jump or jerk between iterations because the animation may need to reset the target property back to the value set within the From attribute. If you want more fluid animations, you may consider having an animation end at, or just before, the value specified within the From value. Alternatively, you may choose to skip setting the From value altogether. Either way, you need to know where the animation is going.
One way to predetermine where an animation is going is by setting the To property. The To property is exposed within the ColorAnimation, DoubleAnimation, and PointAnimation types. This value represents the destination of a specific animation. And like the From property, the value associated with the To property must be compatible with the type of animation. To get a better feel for this property, please examine its use in snippet 9.8.
From=".75" To="0" />
snippet 9.8 shows the Opacity of the Ellipse changing from.75 to 0 when the animation begins. Over the course of one second, the Opacity of the Ellipse will change to 0. If you’ve defined a value for the From attribute, you don’t have to set the To property. Instead, you can rely on the use of the By property.
The By property is a special shortcut that provides an alternate to the To property. Instead of having to know where you want to go initially, you can conveniently specify a value in the By attribute. Then, when the animation is run, Silverlight adds the value defined in the From field to the By value to automatically determine the To value. To get a firmer understanding of how this can be used, please review snippet 9.9.
From=".25" By=".50" Duration="0:0:1" />
This snippet defines the animation for an assumed Image. When the animation begins, the Opacity property of the Image is set to .25. Over the course of one second, you want this animation to increase the Opacity value by .50. When this animation has started, the To value will essentially be .75. You can also decrease the Opacity value by providing a negative value, as shown in snippet 9.10.
From=".25" By="-.10" Duration="0:0:1" />
This snippet shows the alternate to increasing a value. Note that the By property itself is an alternate to the To property. If both of these properties are defined, the To property will take precedence, and the By property will be ignored.
The By and To properties enable you to provide guidance for your animations. These animations begin at the value provided within the From field. To determine how long the animation should take to get to the destination, we have one final question to address.
As mentioned earlier, each animation is a Timeline object, so a number of valuable time-related traits are shared among all animations. The most important of these items is the Duration property.
The Duration property specifies how long it will take for a Timeline to complete a single episode. This value can be defined using the TimeSpan syntax or it can use a predefined value, defined within the Duration struct and described in table 9.1.
|Automatic||Means that a Timeline will automatically end when all child elements have been completed.|
|Forever||Signals that an animation can run forever and ever.|
Table 9.1 illustrates that you have two different options when it comes to controlling the Duration of an animation. To control the playback speed of an animation, call on the SpeedRatio property.
The SpeedRatio property represents the throttle for a Timeline. By default, this double-precision value is set to 1.0. This value can be set to any positive double-precision value and act as a multiplier to the Duration property value. Figure 9.2 shows the Duration, SpeedRatio, and time values for a completed Timeline.
As figure 9.2 illustrates, the SpeedRatio property can have a pretty significant impact on the Duration of a Timeline. These results show that any value less than 1 will slow down an animation. At the same time, any value greater than 1 will speed up the animation. Besides adjusting the speed of an animation, you may need to repeat its performance. For this reason, there’s a RepeatBehavior property.
The RepeatBehavior property is an interesting animal that may act slightly different than you’re anticipating. This property enables you to specify how many times an animation should be played back-to-back. This property also enables you to specify how long the animation should run regardless of the Duration value—the animation will play back-to-back until the time specified in the RepeatBehavior property has elapsed. To get a further understanding of how this property works, please examine figure 9.3.
Figure 9.3 illustrates the effects of the RepeatBehavior property in relation to the Duration of an animation. The first three bars illustrate how to use the RepeatBehavior to specify the total number of times a Timeline should run. The last three bars show how to use the RepeatBehavior to specify a specific length of time for a Timeline.
As shown in the first three bars, you can append an x as the suffix to a positive, double-precision value. This suffix informs Silverlight that you want an animation to run a specific number of times. The total number of times is represented as the value before the x. If the RepeatBehavior is set to 2.0x, the animation will run two times; if the value is set to 5.0x, it will run five times. These types of values can have a significant impact on your animations.
If the value before the x is greater than 1.0, you may notice a jerk between the iterations of the animation because, unless your animation ends at the beginning of an animation, the animation will need to jump to the start to be reset. If the value before the x is less than 1.0, you’ll notice that the animation will stop before the animation has completed because the RepeatBehavior takes precedence over the Duration property. This can have significant implications if you specify a time value as shown in the last three bars of figure 9.3.
By specifying a specific length of time for the RepeatBehavior, you’re informing the Timeline to repeat until the specified time has elapsed. This length of time can be specified using the TimeSpan format. Or, you can specify the Forever value to make the Timeline run until you programmatically force the animation to stop. Either way, at times you may want a more cyclic animation. For these situations, you may want to consider the AutoReverse property.
The AutoReverse property enables you to automatically play a Timeline in reverse after it has played once forward. This Boolean property is, by default, set to false. Changing this property value to true can enable you to deliver a throbbing effect—among other things. Note that changing this property to true can have residual effects on the overall playback of a Timeline.
By setting the AutoReverse property to true, the overall playback time of a Timeline may be doubled. When the AutoReverse property is true, a Timeline isn’t deemed as finished until it plays once forward and once backward. If you’re specifying a number of iterations within the RepeatBehavior property, a single iteration will take twice as long. Once an iteration has completed, you should have the ability to decide how it should behave.
When an animation reaches the end of a Timeline, it normally stays, or holds at the end, but the FillBehavior property gives you the opportunity to determine what to do. When the end is reached, you can order the playback to do one of the options provided within the FillBehavior enumerator. These options and their descriptions are shown in table 9.2.
|HoldEnd||When completed, a Timeline will stay at the end until told otherwise. This is the default value for the FillBehavior property.|
|Stop||Once the Timeline has completed, the playback position will automatically reset to the beginning.|
You have two options: You can either stay at the end or reset to the beginning. But the beginning of a Timeline isn’t necessarily what it may seem. This beginning of a Timeline can be altered by the BeginTime property.
The BeginTime property represents when to start playing a Timeline. In reality, this property sort of behaves as an offset, which can be set using the familiar TimeSpan format. By default, the BeginTime property’s value is set to null, which translates to "0". This setting is why animations begin playing immediately when told to do so. You can set this value to another TimeSpan value to provide an offset, as shown in snippet 9.11.
Storyboard.TargetProperty="Opacity" From="0" To="1"
BeginTime="00:00:5" Duration="0:0:1" />
This snippet shows an Image that fades in over the course of one second. Unlike the previous animations, this one will not start immediately. Instead, once the animation begins to play, it waits until the time specified within the BeginTime property has elapsed. Once this time period has elapsed, the image begins to fade into view. Because of this, you can assume the entire animation in snippet 9.11 takes six seconds to complete.
The BeginTime property may seem somewhat odd. In fact, it is sort of odd if you consider it only in regard to a single animation, but this property provides a significant amount of value when you have multiple animations working together. To make use of multiple animations, you must take advantage of the required Storyboard element.
Every animation created within Silverlight must be defined within a Storyboard. A Storyboard enables you to organize multiple animated parts that work together simultaneously. Often, these animated parts will span different properties across different UI elements. As you can imagine, it only makes sense to have a way to collectively organize and control these animated parts. Thankfully, the Storyboard empowers you to do just that.
A Storyboard is an umbrella under which multiple animations can be defined to address a common scenario. From a development perspective, a Storyboard can be considered as a collection, or grouping of animations. This grouping provides you with a way to easily target and control one or more animations. The syntax for this grouping is shown in snippet 9.12.
<!-- The common animations -->
This snippet shows the basic syntax of a Storyboard. This Storyboard element could have any number of animations placed inside it. You can place other Storyboard elements within it if you so desire because the Children property of a Storyboard represents a collection of Timeline elements. You can add any type of animation or other Storyboard elements because they derive from the Timeline class. Snippet 9.13 shows how you can intertwine types within a single Storyboard.
<Storyboard x:Name="myStoryboard" Storyboard.TargetName="myRectangle">
<DoubleAnimation x:Name="myDoubleAnimation" Duration="00:00:03"
From="0" To="1" />
<ColorAnimation x:Name="myColorAnimation" Duration="00:00:03"
From="Green" To="Blue" />
<Rectangle x:Name="myRectangle" Width="180" Height="60" Fill="Green"
This snippet shows a Storyboard that changes a Rectangle from green to blue as it fades into view. This small sample begins to show the power allotted by the Storyboard. Before we discuss the other powerful features of the Storyboard, let’s look at how to define the target of your animations.
As mentioned earlier, the Storyboard exposes two attached properties that can be used to set the target of an animation. The first property is TargetName, and the second property is TargetProperty. These two property values are codependent and are both required to create an animation. Without these values, your animations won’t know what to animate. If you define these two values within a Storyboard, you can share their values across the child Timeline elements.
As shown in snippet 9.13, the Storyboard uses the TargetName attached property to specify the target of the animation. Each of the child animations uses the same target element. If one of these animations needs to use a different element, you can trump this value by providing a different TargetName value, using the approach shown in snippet 9.14.
This snippet defines the primary target of the Storyboard as myRectangle . This target is used by the animations defined in and . Animation uses myRectangle2 as the target instead of myRectangle. This is accomplished by overriding the TargetName value set in . Note that each of the animations in this snippet targets a separate property.
To target a property within an animation, you use the TargetProperty attached property. As you’ve probably guessed, this attribute allows you to specify which property of the target element should be animated. You can specify the name of this property in a couple of different ways.
The first, and most verbose approach, involves setting the name of the property you intend to animate. This approach is used in and . Generally, this approach will work for most of the properties throughout the Silverlight APIs, but it won’t always be enough.
Consider the situation where you want to change the color of a Brush. Generally, the color of a Brush is defined as a property within a property within a property. This is shown in of our previous code snippet. Although, at first, this may not seem possible within XAML, there’s, in fact, a way.
XAML supports a flexible daisy-chaining model that enables you to access nested properties. This model allows you to access the individual properties by drilling through the hierarchy. To drill down through the hierarchy, you begin at an element type. From there, you access a specific property by using a period as a delimiter. If the property represents a collection, you can access the individual items by using an indexing syntax. To gain a firmer understanding of these syntactical details, please review snippet 9.14.
Snippet 9.15. XAML: Using the daisy-chain syntax to change the individual colors of a LinearGradientBrush
<Storyboard x:Name="myStoryboard" Storyboard.TargetName="myRectangle">
<Rectangle x:Name="myRectangle" Width="120" Height="60" >
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="#FFDA0000" Offset="0"/>
<GradientStop Color="#FFA500BB" Offset="0.25"/>
<GradientStop Color="#FF000000" Offset="0.5"/>
<GradientStop Color="#FFA500BB" Offset="0.75"/>
<GradientStop Color="#FFDA0000" Offset="1"/>
This snippet shows how to use the daisy-chain syntax to access the individual colors used within the LinearGradientBrush. An index of 1 is used within to reference the second color of a transition. At the same time, an index of 3 is used to change the color of the GradientStop used in . In addition to the indexing syntax, it’s important to recognize the use of the parentheses around each property.
Parentheses are used in the daisy-chain syntax to group a property with an element. As shown in snippet 9.15, you can’t begin by drilling into a property; instead, you begin with an element type. From there, you specify the name of the property you want to animate, and continue by delimiting with a period. This syntax is depicted in figure 9.4.
This figure shows the general syntax used for referencing daisy-chained properties. This approach makes it easy to access items that haven’t been explicitly named. This syntax enables you to readily take control of the properties within an element. Equally important is the way that Silverlight enables you to take control of the Storyboard itself.
The Storyboard class exposes a number of valuable methods that empower you to programmatically control an animation. These methods, which are shown in table 9.3, reflect many of the features you’ve already seen within the MediaElement.
|Begin(...)||Turns the hourglass to start pouring the sands of time. This method starts the animations that are the Children of the Storyboard.|
|Pause(...)||Halts the playback of the animations associated with a Storyboard and preserves the current position.|
|Resume(...)||Continues the animations associated with a Storyboard from a previous position.|
|Seek(...)||Skips to a new position within a Storyboard. The position is represented as a TimeSpan value.|
|Stop(...)||Halts the playback of the animations associated with a Storyboard and resets the playback position to the beginning of the Storyboard.|
The methods described in this table enable you to programmatically interact with a Storyboard. In doing so, you can easily deliver a dynamic animation experience. This experience may involve leaping forward to a later part in an animation or giving the user control via interactive playback features. Either way, an important part of interacting with an animation involves knowing when it’s finished. Thankfully, the Storyboard exposes the Completed event.
The Completed event is the only event exposed by the Storyboard element. In reality, this event is part of the Timeline. Regardless, the Completed event is triggered when the assigning Storyboard has finished. A Storyboard is deemed as finished once all its child Timeline elements have completed. Snippet 9.16 shows a MediaElement performing one complete rotation when a user clicks it. Once this animation has completed, it will use another animation to fade the MediaElement out of view.
This snippet shows how you can programmatically use both the Completed event, as well as one of the interactive playback methods. When the user clicks the MediaElement defined in , the Storyboard defined in will begin playing . Once this Storyboard has finished playing, the Completed event associated with it will be triggered . This event handler will then start the animation defined in . Snippet 9.16 also shows how you can define an animation as a resource. This is one of the two ways that you can use an animation on the road to being resourceful.
Storyboard elements enable you to create complex and intricate animations. These animations may be used in response to an event or to something that has occurred behind the scenes. Because of this, you need multiple ways to interact with a Storyboard. Thankfully, Silverlight gives two approaches for organizing Storyboard elements to adequately meet your needs. You can define a Storyboard as either a resource or a trigger.
The first approach for organizing a Storyboard involves defining it as a resource. A resource is an easy way to set aside a commonly used item for reuse. This item, in our case, a Storyboard, can be defined as a resource by creating it within the Resources collection of a UIElement. This can be accomplished by either programmatically adding it through code, or creating it within XAML as shown in snippet 9.17.
This snippet shows how easy it is to define a Storyboard as a resource. The definition of the Storyboard is placed within the Resources collection of the root Canvas. The root element of a Silverlight page is generally where you’ll place your resources because it makes the resources accessible to all the elements within the page. Thankfully, the Resources collection can store as many, or as few, resources as you need.
Once a Storyboard is defined as a resource, it’s your responsibility to start it. You must first programmatically retrieve it. This step involves either referencing the Storyboard by name or retrieving it through the FindName method. Snippet 9.18 shows the Storyboard from snippet 9.17 being retrieved via the FindName method, and then programmatically starts it via the Begin method.
Storyboard myStoryboard = (Storyboard)(this.FindName("myStoryboard"));
This snippet illustrates how simple it is to programmatically start a Storyboard defined as a resource. Notably, because the Storyboard had the x:Name attribute assigned, the FindName method was not necessary. Instead, you could have used myStoryboard.Begin(). The FindName approach was used to show that it could be used in this case. Regardless, there are certain times when you know that a specific action should automatically start a Storyboard. For these situations, Silverlight provides an elegant shortcut that enables you to automatically start a Storyboard when a defined event occurs.
The second approach for defining a Storyboard involves setting it as an event handler. An EventTrigger is a special element that enables you to declaratively define a response for a specified event. When this event occurs, the EventTrigger automatically starts the defined Storyboard. To accomplish this, you follow a few simple steps.
First you decide which event you want to respond to. Currently, the only event supported within the EventTrigger is the Loaded event. To specify this event as the triggering event, you must identify the type of object responsible for the event. Once identified, you can set it, as well as the event, through the RoutedEvent property as shown in snippet 9.19.
<!-- Insert Actions here -->
As this snippet shows, the RoutedEvent property uses a syntax that resembles element-TypeName.eventName. The type name comes from the parent type. Generally, you’ll be able to retrieve this type name from the attached property containing the trigger. This attached property is called Triggers, and it’s available from all UIElement objects. If you were to expand on our previous code snippet, you should have something like snippet 9.20.
<!-- Insert Actions here -->
This snippet shows how the EventTrigger has been added to a Canvas. Significantly, this does not mean that the target of the Storyboard will be the Canvas. Instead, as discussed earlier, the target of the Storyboard is set within the Storyboard itself. To set the target of the Storyboard, you first define the Storyboard.
If you’re defining a Storyboard within an EventTrigger, you must associate it with an action. Currently, Silverlight only provides one action called BeginStoryboard, which starts a Storyboard when called upon. You must use this action if you’re creating an EventTrigger. To put all the pieces together, defining a Storyboard as a trigger would look like snippet 9.21.
<Canvas Width="100" Height="100" Background="White">
From="0" To="1" />
This snippet shows a Storyboard defined as a trigger. But, until Silverlight matures, you’ll most likely define your Storyboard elements as resources. In fact, the official Silverlight documentation included with the SDK recommends against using a trigger. Either way, the Storyboard provides a way to logically organize your animations. These animations are all about changing a visual property over time. As you’ve seen, this process works in a linear fashion. To create even more dynamic visual effects, it’s important to consider using a technique known as key framing.
In the realm of traditional animation, animators will often present a high-level overview of a story by drawing out the main images. These images generally represent the beginning and ending points of a transition. These end points represent the key frames within an animation. Once the key frames are created, the process of creating the animation in between them is fairly straightforward. Within software, this process of creating the in-between frames is known as interpolation.
To firmly grasp the concept of how key frame animations can be used, let’s consider the task of animating a bouncing ball. If you were to attempt to animate an ellipse, the ball may look like that in figure 9.5 over some period of time.
The arrows shown in this figure represent two things within the animation. They represent the direction that the ball is bouncing and they represent the parts of the animation created via interpolation. This process of interpolation enables you to ignore having to define the To, From, and By property values you were using earlier. Instead, you must create a KeyFrame for each discrete location within an animation. Snippet 9.22 shows the XAML to reproduce the animation shown in figure 9.4.
<Canvas Width="640" Height="480" Background="White">
<Ellipse Width="50" Height="50" x:Name="myEllipse"
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="77" />
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="148" />
<LinearDoubleKeyFrame KeyTime="00:00:03" Value="223" />
<LinearDoubleKeyFrame KeyTime="00:00:04" Value="315" />
<LinearDoubleKeyFrame KeyTime="00:00:05" Value="397" />
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<LinearDoubleKeyFrame KeyTime="00:00:01" Value="132" />
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="42" />
<LinearDoubleKeyFrame KeyTime="00:00:03" Value="132" />
<LinearDoubleKeyFrame KeyTime="00:00:04" Value="81" />
<LinearDoubleKeyFrame KeyTime="00:00:05" Value="132" />
This snippet illustrates the general syntax of a KeyFrame. This example uses two key frame animations to move an Ellipse around the Canvas. The new position of the Ellipse is interpolated between the values specified within the Value property of each KeyFrame. The KeyFrame determines how to interpolate these values by referring to the type of KeyFrame.
The type of KeyFrame always follows a naming template that mimics [interpolation-Type] propertyType KeyFrame. This syntax specifies the type of property that’s the target of the animation. In addition, this syntax specifies what type of interpolation should be used to generate the in-between values. To simultaneously address both of these important items, Silverlight provides the key frame types shown in table 9.4.
Discrete key frame types
Linear key frame types
Spline key frame types
Each type of key frame helps to address specific animation scenarios. To understand when a specific type of animation is relevant, it’s important to understand the various types of interpolation.
An interpolation type gives you control over how an animation will accelerate, or decelerate, as it progresses. The interpolation type signals how an animation should estimate the values in between key frames. To estimate the values as you see fit, Silver-light provides three interpolation types: linear, spline, and discrete.
Linear interpolation constructs the most direct transition between two key frames. The linear descriptor is used because the change between two key frames occurs at a constant, linear rate. Figure 9.6 shows an object moving between several points using linear interpolation.
The idea of using an animation that occurs at a constant, predictable rate at first seems appealing. But, as this figure shows, you can easily end up with a jerky or jagged animation. This jarring can leave users feeling like they’re riding an old, wooden roller coaster. This effect occurs because the transition between two linear key frames occurs in distinct states. These stages may be desirable; but, if they aren’t, there’s a way to create even smoother transitions thanks to spline interpolation.
Splines are generally used to create smooth, seamless transitions. These transitions occur by estimating the values as if they were generated along a Bezier curve. This curve represents the values to use within a time segment. To illustrate, figure 9.7 shows a curved interpolation.
If you compare this figure to figure 9.5, you can see how using splines allows you to create a much smoother transition between key frames. Note that the line in this figure does not represent the path that the ball travels along. Instead, the line gives the illusion of varying speeds. These varying speeds are controlled through the Key-Spline property.
The KeySpline property enables you to control the progress of an animation through two control points, which determine the curve that the values are interpolated along. By default, this curve resembles a line. To generate values along something other than a line, you must understand how the KeySpline relates values to points in time. This relationship, as well as the KeySpline syntax, is shown in figure 9.8.
Figure 9.8. The relationship between time and value as used by the KeySpline property. In this figure,
This figure shows the default “curve” defined by the KeySpline property. The two control points used in this figure are specified as "0.0,0.0 1.0,1.0". These control points always follow a syntax that mimics “x1,y1 x2,y2”. In addition, each coordinate within each point is specified as a positive double-precision value between 0.0 and 1.0. Anything outside of this range will create a runtime error.
The first point defined within the KeySpline property determines how values will be generated along the first half of the curve. The second point defined within the KeySpline property determines how values will be created along the second half of the curve. Either way, if the y value is greater than the x value, the animation will run more quickly. Alternatively, if the x value is greater than the y value, the animation will run slower. Figure 9.9 shows some sample curves along with their respective Key-Spline values.
The curves shown in this figure represent potential curves you can use for interpolating values. In reality, you’ll need to play with these values until your key frame animation feels right. Sometimes, an animation may feel right if the transitions are more discrete.
Occasionally, you may have to create an animation that jumps between values. These rifts seem counterintuitive within the realm of animation because animations are generally considered to be smooth. But, what if you were creating an animation that depicted a Whac-a-Mole game? In a Whac-a-Mole game, small critters appear at random from dark holes. This surprising effect can be effectively re-created using discrete interpolation.
When discrete interpolation is used, Silverlight generates sudden changes between two key frames. These sudden changes make it appear as if the interpolation doesn’t occur at all. That’s because the interpolation process does not occur. Figure 9.10 illuminates how the discrete method interpolates.
This illustration is quite difficult to make sense of. Everything seemingly occurs at random, just like Whac-a-Mole. Although randomness has its place, you often need control over when a key frame occurs. Luckily, there’s is a property that allows you to do just that. This property is called KeyTime.
The KeyTime property of a KeyFrame represents the time at which the value specified within a KeyFrame will be reached. In a sense, the KeyTime sort of represents a bookmark within an animation. But, the position of this bookmark is completely dependent on the TimeSpan value you use.
By providing a TimeSpan value, you can specify the exact point in time that a KeyFrame should be reached. This point in time is relative to the beginning of the animation that the KeyFrame is defined within, so the order of the key frame elements is irrelevant. But, this value still has to be assigned to the KeyTime property, as shown in snippet 9.23. In addition, the snippet shows an illustration of how the animation would be rendered.
Snippet 9.23. XAML Result: Using a TimeSpan value to specify the KeyTime. Each car represents the location of the image when the key frame is reached.
This snippet shows the typical approach for defining KeyFrame elements within an animation . The KeyTime value in each KeyFrame is set to a TimeSpan value. This approach provides a convenient and verbose way to perform an animation. This snippet shows how important the KeyTime property is in key frame animations, which are the types of animations created by Expression Blend. Although Expression Blend is an excellent tool to use for creating animations, it is outside the scope of this book. We highly recommend taking a look at it for your animation needs.
Throughout this chapter, you saw the details associated with animating elements within Silverlight. When it comes down to it, it’s really about manipulating a single property over some time interval. This time interval can be specified within either an animation or, higher up the tree, a Storyboard. The Storyboard enables you to organize and control multiple animations simultaneously, empowering you to create incredibly dramatic and engaging effects. With the help of key frames, these effects can be developed extremely quickly and efficiently.
Providing an engaging, soothing user experience can be a valuable addition to any application. Providing a consistent user experience is perhaps equally, if not more, valuable. To help provide a consistent user experience, Silverlight provides a variety of style and template features. These features are discussed in detail in chapter 10.