WPF animations are described through the use of Storyboard objects. They’re nice because a designer can create them in Blend and nobody has to mess with their XAML. Write a couple storyboards manually and you’ll see just how important that is.
But storyboard objects animate only with respect to the simple passage of time. This bothers me. What if I want to play an animation in response to mouse movement, or something scrolling? Here’s an example. Suppose I have an app that has a cool mechanical theme, with gears moving and things spinning like the app itself is some kind of machine. The app has a scrollable region, with a scrollbar. I want some gears attached to that scrollbar, and I want them to turn, not constantly over time, but in response to me adjusting the scrollbar’s value. That’d be much more interactive and fun.
Fundamentally, what I want is the ability to map an artibrary range of values (in this example, the range of my scrollbar) to a storyboard’s timeline, and then bind that value to the animation.
While I am a little disappointed that Microsoft doesn’t have any means of implementing your own animation clock directly (at least, not that I could find), it’s actually not that hard to set this up. Care must be taken, however, because normally animations occur on another thread (that seems to be lower priority than the UI thread). Thankfully Microsoft gives us a means to seek to a specific position along the timeline synchronously through Storyboard.SeekAlignedToLastTick.
My implementation defines 4 properties: Storyboard, Value (used in place of a clock), and Minimum/Maximum, which provide boundaries for the Value property. Most of the class is just plumbing, and you can download it using the link below. But the meat of it involves finding the duration of the storyboard, and seeking to the right timeline position when Value changes.
Finding the duration of a storyboard is strangely harder than it ought to be. Here’s what worked well for me:
Storyboard.Begin();
Storyboard.SeekAlignedToLastTick(TimeSpan.FromMilliseconds(
TimeSpan.MaxValue.TotalMilliseconds - 1));
double dur = Storyboard.GetCurrentTime().TotalMilliseconds;
Storyboard.Stop();
return dur;
Annoying. But, it works, so I’ll take it. Now, when Value changes, the storyboard timeline needs to be updated to reflect that. Here’s what that looks like:
Storyboard.SeekAlignedToLastTick(TimeSpan.FromMilliseconds(_sbDuration * (Value - Minimum) / (Maximum - Minimum)));
What’s it good for? In addition to binding to things like slider values, scrollbar positions, and progress bar values, you can use it to animate in response to user input. Maybe bind it to the position or velocity of the mouse. In my case, I intend to use it in response to a user’s movement from the Kinect. Animation like this provides feedback to the user that can be invaluable in providing discoverability to your application.
The source is available here.
[...] Evan’s Code Clunkers WPF animations are described through the use of Storyboard objects. They’re nice because a [...]