Modifying Animations using C# - Page 4
       by kirupa  |  14 May 2008

We are almost there! In the previous page, we started to add some code, and I provided and explained the code needed to get the selected value from our combo box. We left everything at a bit of a cliffhanger with this mysterious call being made to a nonexistent ModifyAnimation method. Let's pick up from there!

Changing the KeyTime Values
Now that we have our combo box setup, let's figure out how to change the value of each of our keyframes' KeyTime value. First, because I am sure it is bugging you, add the following new method called ModifyAnimation that takes a double as its argument:

private void ModifyAnimation(double incrementer)
{
 
}

If you recall, the ChangeSpeed method calls ModifyAnimation with our incrementer value as the argument. We just created that method above. If you run your application, nothing noticeable will happen when you change the selected values in your combo box.

What we are going to do now is alter the KeyTime values stores in each of our keyframes. Here is the game plan. We have no way of directly targeting the keyframes. What we have to do is first get a reference to our Storyboard, then get a reference to the Animation, and then get to the Keyframes!

Because this will require us traversing through our Storyboard, for reference, below is the XAML for our animation:

<Storyboard x:Key="Oscillate">
<DoubleAnimationUsingKeyFrames
   BeginTime="00:00:00"
   RepeatBehavior="Forever"
   Storyboard.TargetName="ellipse"
   Storyboard.TargetProperty="(UIElement.RenderTransform).
          (TransformGroup.Children)[3].(TranslateTransform.X)"
>
 
 
<SplineDoubleKeyFrame
KeySpline="1,0,1,1"
KeyTime="00:00:00"
Value="0"/>
<SplineDoubleKeyFrame
KeySpline="0,1,1,1"
KeyTime="00:00:01"
Value="-130"/>
<SplineDoubleKeyFrame
KeySpline="1,0,1,1"
KeyTime="00:00:02"
Value="0"/>
<SplineDoubleKeyFrame
KeySpline="0,1,1,1"
KeyTime="00:00:03"
Value="140"/>
<SplineDoubleKeyFrame
KeySpline="1,0,1,1"
KeyTime="00:00:04"
Value="0"/>
 
</DoubleAnimationUsingKeyFrames>
</Storyboard>

Let's start adding some code. First, let's get a reference to our Storyboard:

private void ModifyAnimation(double incrementer)
{
Storyboard sb = this.FindResource("Oscillate") as Storyboard;
}

Because our Storyboard is stored as a Resource, I use the FindResource method and pass in the key of our storyboard, which is Oscillate. Because FindResource returns values only as the object type, I cast it as a Storyboard by using the VB-like as Storyboard syntax.

Next up is the code for getting the reference to our animation:

private void ModifyAnimation(double incrementer)
{
Storyboard sb = this.FindResource("Oscillate") as Storyboard;
DoubleAnimationUsingKeyFrames animation = sb.Children[0] as DoubleAnimationUsingKeyFrames;
}

Your storyboard stores children that are animations. In our case, our storyboard has just one child - the DoubleAnimationUsingKeyframes. Therefore, I pass in the index position of 0 to the Children collection of our storyboard and cast the resulting value as that of our DoubleAnimationUsingKeyframes object.

The thing to note is that I would not know all of this unless I looked at the XAML. Depending on your animation, your animation could be a host of different types besides DoubleAnimationUsingKeyframes. Because I have the XAML directly in front of me, I can feel safe declaring a new object of type DoubleAnimationUsingKeyframes and deliberately retrieving the first item from our storyboard's Children collection without taking edge cases into account. If I were to modify my animation such that these assumptions no longer hold true, then I would have to make sure I update this code as well.

Our animation has keyframes, so what we are going to do next is figure out a way to gain access to them:

private void ModifyAnimation(double incrementer)
{
Storyboard sb = this.FindResource("Oscillate") as Storyboard;
DoubleAnimationUsingKeyFrames animation = sb.Children[0] as DoubleAnimationUsingKeyFrames;
DoubleKeyFrameCollection keyFrames = animation.KeyFrames;
}

Unlike before were we call the Children property of a collection, your animation doesn't store the Keyframes as children. Instead, the keyframes are stored in their own KeyFrames property, and you can access them by calling your animation's KeyFrames property as shown above.

Your keyFrames object now stores a collection of keyframes that you can iterate through, so let's add a for loop: 

private void ModifyAnimation(double incrementer)
{
Storyboard sb = this.FindResource("Oscillate") as Storyboard;
DoubleAnimationUsingKeyFrames animation = sb.Children[0] as DoubleAnimationUsingKeyFrames;
DoubleKeyFrameCollection keyFrames = animation.KeyFrames;
for (int i = 0; i < keyFrames.Count; i++)
{
SplineDoubleKeyFrame keyFrame = keyFrames[i] as SplineDoubleKeyFrame;
keyFrame.KeyTime = new TimeSpan(0, 0, (int)incrementer * i);
}
}

Because we have five keyframes, keyFrames.Count will return 5. What we do in each of those five iterations is first, get the keyFrame and cast it as a SplineDoubleKeyFrame, and second, set the KeyTime to a new TimeSpan whose second value is our loop's index position i multiplied by the incrementer value we pass in.

After you have finished adding the above line of code, you are pretty much done. If you hit F5 and run your application, your animation will start playing. Each time you change the speed value in your combobox, does the animation's speed actually change? No, the speed does not, and let's find out why and how to fix this on the next page.

Onwards to the next page!

1 | 2 | 3 | 4 | 5




SUPPORTERS:

kirupa.com's fast and reliable hosting provided by Media Temple.