While the various animation classes that extend the Timeline class can be used to animate control properties directly in code, in order to declare and trigger animations using XAML alone, we need to use the Storyboard class. This is what is known as a container timeline, as it extends the abstract TimelineGroup class that enables it to contain child timelines.
Another container timeline class that the Storyboard class extends is the ParallelTimeline class and these classes enable us to group child timelines and to set properties on them as a group. When creating more complex animations, if all we need to do is to delay the start of a group of child timelines, we should use the ParallelTimeline class rather than the Storyboard class, as it is more efficient.
We could rewrite our earlier BeginTime example to use a ParallelTimeline element to delay the start of our last two timelines. Let's see what that might look like:
<Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="300.0" Duration="0:0:2" /> <DoubleAnimation Storyboard.TargetProperty="Height" To="300.0" Duration="0:0:2" BeginTime="0:0:2" /> <ParallelTimeline BeginTime="0:0:4"> <DoubleAnimation Storyboard.TargetProperty="Width" To="0.0" Duration="0:0:2" /> <DoubleAnimation Storyboard.TargetProperty="Height" To="0.0" Duration="0:0:2" /> </ParallelTimeline> </Storyboard>
As the Storyboard class is a Timeline object, it also has the same properties as the various animation objects. One additional property that it inherits from the ParallelTimeline class is the SlipBehavior property. This property is only really useful when we want to synchronize an animation timeline with the playback of a MediaTimeline element, but it's worth knowing about.
This property is of the enumeration type SlipBehavior and it only has two members. A value of Grow specifies that we do not need our animation timelines to be synchronized with our media timeline(s) and is the default value of this property.
Conversely, a value of Slip indicates that we want our animation timelines to slip, either forwards or backwards, whenever necessary in order to keep them in sync with the playing media. If the media takes time to load when using this setting, then the animation timelines within the storyboard will wait until the media is ready and continue at that point.
In addition to the properties that have been inherited from the various base classes, the Storyboard class also declares three important Attached Properties that are essential for targeting animations to individual UI elements and/or their properties.
The Storyboard.Target Attached Property specifies the UI control that should be animated, although setting this property alone is not enough, as it does not specify the target property. This property is of type object, although it can only be used with objects of type DependencyObject.
In order to use this property, we need to specify a binding path that references the target UI control. If the target element extends the FrameworkElement or FrameworkContentElement classes, then one way would be to name the target element and to use an ElementName binding to reference it:
Storyboard.Target="{Binding ElementName=TargetControlName}"
Most UI elements extend one of these two classes that declare the Name property. However, if we provide a name for the target control, then there is a simpler way to target it. Instead of using the Storyboard.Target property, we could use the Storyboard.TargetName Attached Property to specify the target element using just their declared name, without any binding:
Storyboard.TargetName="TargetControlName"
We do not always need to specify this property value, as on occasion, the target element can be worked out implicitly. If the relevant storyboard was started with a BeginStoryboard element, the UI element that declared it will be targeted. Additionally, if the relevant storyboard is a child of another timeline, then the target of the parent timeline will be inherited.
The most important property that the Storyboard class declares is the TargetProperty Attached Property. We use this property to specify which property we want to animate on the target element. Note that in order for this to work, the target property must be a Dependency Property.
Occasionally, we may want to target objects that do not extend either of the framework classes mentioned earlier; in WPF, we are also able to target freezable classes that extend the Freezable class. In order to target one of these classes in XAML, we need to specify the name of the object using the x:Name directive instead, as they have no Name property.
As a side note, WPF classes that declare their own Name property actually map the name value through to the x:Name directive, which is part of the XAML specification. In these cases, we are free to use either of these to register a name for an element, but we must not set both.
Note that unnamed elements can still be referenced by our animations, although they need to be indirectly referenced. Instead of referencing them directly, we need to specify the name of the parent property or freezable object and then chain properties in the TargetProperty Attached Property until we reach the desired element. We used this method in the last example of the previous section:
Storyboard.TargetProperty="Fill.GradientOrigin"
In this case, we reference the Fill property, which is of type RadialGradientBrush, and then we chain to the GradientOrigin property of the brush from there. Note that if we had used an instance of the SolidColorBrush class here instead, this reference would fail, because there is no GradientOrigin property in that brush. However, while the animation would fail to work, this would not cause any errors to be raised.