Cover

Animating Brushes with ObjectAnimationUsingKeyFrames

July 18, 2008
No Comments.

Url: http://wilderminds.blob.core.windows.net/downloads/StateBrushes.zip
Silverlight Logo
In my recent class I had a student ask me about animating a brush. I quickly dove into how to animation a *color* in a brush and he indicated that isn’t what he wanted.  He had a resource-based brush that he wanted to switch out in the Visual State Manager.  In Blend when he was setting the Brush during the animation recording of the MouseOver event but that wasn’t creating a storyboard but changing the natural brush of the control. To solve this we had to dive into the XAML as Blend doesn’t seem to know about the ObjectAnimationUsingKeyFrames animation type.

First a small discussion about animations.  Normally animations will take a value and transition between the values.  For example a **DoubleAnimation **that is set up to change the Opacity of a visual element from zero to one, will interpolate the values to create a smooth transition from invisible to visible. Keyframed animations support several types of interpolation including linear (the default), spline and discrete.  Linear keyframes simply create a smooth transition between values.  Spline keyframes allow you to shape the interpolation between the values to ease-in or ease-out the transition.  Lastly, the Discrete keyframes set a value at the time of the keyframe without any interpolation.

The **ObjectAnimationUsingKeyFrames **animation allows you to use object instead of primitive values for your animation keyframes. The only type of keyframe allowed in the **ObjectAnimationUsingKeyFrames **animation are Discrete keyframes.  The reason is that there is no good way to interpolate between object values. So back to our original problem, animation a Brush.  In the MouseOver state, the student wanted to change the Fill of a rectangle from one resource-based Brush to another.  For example, here are the brushes defined in the Resources section:

<UserControl.Resources>
  <LinearGradientBrush x:Key="backBrush"
                       EndPoint="0.5,1"
                       StartPoint="0.5,0">
    <GradientStop Color="#FFFF0000" />
    <GradientStop Color="#FFE50000"
                  Offset="1" />
  </LinearGradientBrush>
  <LinearGradientBrush x:Key="mouseOverBackBrush"
                       EndPoint="0.5,1"
                       StartPoint="0.5,0">
    <GradientStop Color="#FF006DFF" />
    <GradientStop Color="#FF0064E9"
                  Offset="1" />
  </LinearGradientBrush>
  
  ...

The student had set the backBrush to the Rectangle’s fill during the design phase and wanted to set the mouseOverBackBrush during the Visual State Manager’s MouseOver state. Since Blend wouldn’t let us do it, we had to write the ObjectAnimationUsingKeyFrames XAML ourselves.

To do this, we created a Storyboard and an ObjectAnimationUsingKeyFrames element inside the MouseOver state (of the Visual State Manager for our Button’s ControlTemplate).  In the new element we specified the Storyboard.TargetName and Storyboard.TargetProperty to “theBack” and “Fill”. Inside the animation, we created a DiscreteObjectKeyFrame for our one value.  We just wanted to change it to the since new brush so we only needed one.  We set the Keytime to “0:0:0” to specify that this should happen immediately.  Lastly, we specified the StaticResource for the mouseOverBackBrush as the Value. Here’s the entire MouseOver state XAML:

...
<VisualState x:Name="MouseOver">
  <Storyboard>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="theBack"
                                   Storyboard.TargetProperty="Fill">
      <DiscreteObjectKeyFrame KeyTime="0:0:0"
                              Value="{StaticResource mouseOverBackBrush}" />
    </ObjectAnimationUsingKeyFrames>
  </Storyboard>
</VisualState>
...

We didn’t need to specify another animation in the Normal state the Visual State Manager automatically handles the resetting of our value back to the original state. It just works.  Here’s two pictures of the button before and after the mouse over:

You can download the example here.