Creating new animation types
It’s a fairly uncommon scenario to want to animate a type that’s not already built into WPF. But when you do, it takes quite a lot of work, mostly hacking it with Reflector to explore this undocumented venue.
One day I needed to animate the CornerRadius of a Border. Don’t ask me why, it just happened. I was a bit surprised to find out that almost all animations have the (almost) exact same implementation, but no code was shared. Meaning, if you had to create a new one, all the code would have to be copied.
All animations inherit from AnimationTimeline. Each one has a base class T_AnimationBase, and a few possible implementations: _T_Animation (a linear animation), _T_AnimationUsingKeyFrames and (the more rare) _T_AnimationUsingPath. Note the _T. Does it remind you of something? If you said “generics”, you were right. I may be overlooking something, since WPF is such a vast framework, whose designers must have had a much broader view to make such decisions, but I think this could have been implemented a bit better. Here’s how.
When I wrote a paper about CLR generics for my university, I stumbled upon a problem some of you may have also encountered: How can I use arithmetic operators on a generic type? In C++ it’s rather easy: the compiler checks the template by call when it performs the macro-expansion. So if you write:
template<class T> class MyClass
{
T MyMethod(T a, T b) { return a + b; }
}
That would compile just fine. It would only throw compilation errors when you try to instantiate MyClass<T>
where T doesn’t support the +
operator. In C#, you simply can’t do that (unless you use casting and a lot of ifs - an ugly solution), since you can only constrain using interfaces and base classes. Because numeric types have no such common ancestor, you’re in a pickle.
Anders Hejlsberg has lectured about a possible solution for this issue: create a calculator class. I shall demonstrate using the calculator interface I created for the animation:
public interface IAnimationCalculator<T>
where T : struct
{
T Add(T value1, T value2);
T Subtract(T value1, T value2);
T Scale(T value, double factor);
T Interpolate(T from, T to, double progress);
T GetZeroValue(T baseValue);
double GetSegmentLength(T from, T to);
bool IsValidAnimationValue(T value);
}
I constrained T
to be a value type. I assumed most of the types we’d like to animate are lightweight structures, but this restriction can be removed. And so, I created the other classes, according to the scheme I mentioned above:
Implementers have very few things to do: create an IAnimationCalculator for their type, inherit from the desired classes, override the abstract methods (very few and usually freezable-related, and of course the CreateCalculator()
method) and add constructors (just call the ones from the base.) In the attached project you’ll find a sample implementation for the CornerRadius type.
One last caveat: I did not test this on a type other than CornerRadius. It’s quite possible this small framework will not fit every scenario. As I said, the designers of WPF may very well have had a good reason not to take this approach.
Update: I’ve changed the implementation from a calculator type parameter to a CreateCalculator()
abstract method. This makes my classes slightly more cumbersome, but more importantly, makes writing polymorphic code a lot nicer (consider writing a method that accepts a LinearAnimationBase
. Why should you have to drag along that TCalc type parameter?)