navigation
 Monday, June 02, 2008

Anybody familiar with WPF's storyboard knows that animating with the various UI elements is not a problem at all, but what happens when you want the storyboard to call some code behind? The most important thing to remember about the storyboard is that it can only affect dependency properties. So how do we use this to help us call some code behind? We first need to create our own custom dependency property in the form where the storyboard exists.

Define the static DP.

static DependencyProperty DiscreteValueProperty;

Implement a non-static wrapper property that will be called by the storyboard.

[DescriptionAttribute("DiscreteValue")]
[CategoryAttribute("DiscreteValue Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public double DiscreteValue
{
    get { return (double)base.GetValue(DiscreteValueProperty); }
    set { base.SetValue(DiscreteValueProperty, value); }
}

Now create a storyboard in your resources.

<Storyboard x:Key="keySB" SlipBehavior="Slip" >
    <MediaTimeline BeginTime="00:00:00" Storyboard.TargetName="media"/>
</Storyboard>
 
We will use WPF ICommands to do the code behind calling. We need a place to store our commands and tie them to a specific time in the storyboard. To do this we define two members.
 
DoubleAnimationUsingKeyFrames _frames = new DoubleAnimationUsingKeyFrames();
Hashtable _events = new Hashtable();

The DoubleAnimationUsingKeyFrames is a collection of key frames where we can store a specific double value with a event time. In our case we can just store the event time as the double value. We will use the Hashtable to store our ICommand with a particular event time as a key. Now we need to add our new frames collection to our storyboard.

Storyboard _sb = (Storyboard)this.FindResource("keySB");
_sb.Children.Add(_frames);  

With all of that we need to initialize our dependency property.

const string KEYFRAME_PROPERTY_NAME = "DiscreteValue";
 
// register the property metadata
PropertyMetadata metaData = new PropertyMetadata(new PropertyChangedCallback(DiscreteValueChanged));
 
DiscreteValueProperty = DependencyProperty.Register(KEYFRAME_PROPERTY_NAME, typeof(double), typeof(Main), metaData);
 
// register this property with the storyboard manager
Storyboard.SetTargetName(_frames,this.Name);
Storyboard.SetTargetProperty(_frames, new PropertyPath(KEYFRAME_PROPERTY_NAME));
 
Notice that we set up an event handler for any time our dependency property is changed.
If you're familiar with storyboards and how they continually update dependency properties from one value to another, you may be wondering how we are going to get it to call our ICommand just once and at the correct time. We will use the DiscreteDoubleKeyFrame key frame to do this for us. This key frame will set our dependency property with a discrete double value and leave it there until it is set to another double value.
 
To tie all of this together, we need two more functions. One is the property changed event handler and the other is a function to add ICommands to our storyboard.
 
public void AddEventHandler(KeyTime eventTime, ICommand command)
{
   List<ICommand> commands;
   double key = (eventTime.TimeSpan.TotalSeconds);
   if (_events[key] == null)
   {
      commands = new List<ICommand>();
      commands.Add(command);
      _events.Add(key, commands);
   }
   else
   {
      commands = (List<ICommand>)_events[key];
      commands.Add(command);
   }
   DiscreteDoubleKeyFrame frame = new DiscreteDoubleKeyFrame(key, eventTime);
   _frames.KeyFrames.Add(frame);
}
 
public void DiscreteValueChanged(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
   double key = (double)args.NewValue;
   List<ICommand> commands = (List<ICommand>)_events[key];
   if (commands != null )
      foreach (ICommand command in commands)
      {
         if (command != null && command.CanExecute(null))
            command.Execute(args);
      }
}

Notice that we are using our hashtable to store a List<ICommand> for each key time. That way we can load up any particular key time with any number of ICommands. In the the property changed event handler, we just take the double value (which is our key time) and use it to look up our list of ICommands in our hash table. The last step is to iterate through the list of ICommands found at that key time and execute each one.

 |  | 
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, i, strike, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview