Silverlight: Animated “Turbo Tax” style number display

Download Animated Number Project

If you’ve used Turbo Tax, you may have noticed the prominent “Federal Refund” box that’s ever-present at the top of the page while you’re figuring out your taxes. I like this UI concept because when most people are filing their taxes, there’s only one number they actually care about and they care about it every step of the way.

One nice little touch about the display is that whenever you make changes to your return that affect the federal refund, instead of the field just changing immediately, it has a nice incrementing (or decrementing if you’re like me) animation. It is a subtle effect that draws your attention to the field whenever it changes. Try the working example below.

Recently I worked on a line of business application that would benefit from a similar UI. The application is for pricing out sales proposals and takes into account cost of goods, retail price, agent commission, etc. At every step of the way, slight changes to the various inputs affect the bottom line which is the profitability of the deal. So the profitability is always displayed on the screen. When the value changes, I wanted to have this smooth animation between numbers just like Turbo Tax. Turns out it isn’t that hard.

Before we begin

I just want to note that if I just wanted to create a one-off solution, it could probably be done in about 10 lines of code with event handlers. The purpose of this article is to create a control that is effortless to use and re-use. If you want, you can just skip to the sample project and look at the code. It’s really not that complex.

Creating the Control

The idea is very simple. A control that lets you bind a numeric value to it and when that value changes, the displayed number should quickly increment or decrement to the new value instead of changing immediately. The example above shows the finished control in action.

At first I didn’t want to do this as a control. I thought maybe there was some way I could do it at the binding level or with an attached property but that didn’t seem to be possible via any of the extensibility points offered by Silverlight. So I decided to make it a control.

What kind of control should it be? Should it derive from ContentControl? TextBlock? Neither of those seemed appropriate because in order to make it work, I would need a strongly typed Value property. Having a simple Content property based on System.Object would imply that it could accept any type. I didn’t want to go crazy supporting different types. For example, there’s no point in making it able to animate between two DateTime values or TimeSpan. I decided to stick with System.Double as the type of the Value property. Any numeric type would be convertible to/from Double and there’s a built in DoubleAnimation that I could take advantage of.

I settled on creating a new class called AnimatedNumber that derives from Control (not ContentControl.) I added two dependency properties:

public class AnimatedNumber : Control {
    // the actual code defines theese as dependency properties
    public double Value { get; set; }
    private double AnimatedValue { get; set; }
}

The idea here is that the Value property is the bindable public property that would be set to a discrete value such as 0, 100, 200, etc. When this happens we will animate the private AnimatedValue property so that over a short period of time, the AnimatedValue property will quickly change from 0, 1, 2, … 100, … 198, 199, 200.

To do this, I included a PropertyChangedCallback with the ValueProperty registration. This callback is invoked whenever the Value property is changed (either via x.Value = 100 or as a result of a binding expression.) In the callback, we’ll run a Storyboard against the AnimatedValue property that uses a DoubleAnimation to interpolate between the old and new values.

private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    var senderControl = (AnimatedNumber)sender;
    senderControl.AnimateValue((double)e.NewValue);
}

private void AnimateValue(double newValue)
{
    var animation = new DoubleAnimation();
    animation.Duration = TimeSpan.FromSeconds(1);
    animation.To = newValue;

    var storyboard = new Storyboard();
    storyboard.Duration = TimeSpan.FromSeconds(1);
    storyboard.Children.Add(animation);

    Storyboard.SetTarget(animation, this);
    Storyboard.SetTargetProperty(animation, new PropertyPath("AnimatedValue"));

    storyboard.Begin();
}

So now we have a control that exposes a Value that when changed, triggers an incremental change to the new value in a corresponding AnimatedValue property. But so far, the control has absolutely no visual representation! We’ve only defined the behavior of the control.

Creating a Default Control Template

To add a default control template, add a folder named “Themes” to the root of the project and create a resource dictionary named Generic.xaml. Add a style with no key and a setter that defines a very basic control template. In fact, the only thing this control has is a ContentPresenter that will present the AnimatedValue property. The entire Generic.xaml is only a few lines long. The control doesn’t need anything more than that because we eventually want to use it wherever we’d place “content” such as in a button or label. Let some other control provide the eye candy.

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:Local="clr-namespace:AnimatedNumberDemo">
  <Style TargetType="Local:AnimatedNumber">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="Local:AnimatedNumber">
          <ContentPresenter x:Name="PART_Content" />
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>

The important thing is that we have a ContentPresenter named (exactly) PART_Content. You’ll see why in a second.

To make Silverlight associate the above template with our control, we need to add a line to the constructor, and while we’re at it, override OnApplyTemplate. This is the best place to grab the template part we expect to find (PART_Content) and bind it to the AnimatedValue property.

// Template part
private ContentPresenter PART_Content;

// Default public constructor
public AnimatedNumber() {
    DefaultStyleKey = typeof(AnimatedNumber);
}

protected override void OnApplyTemplate() {

    base.OnApplyTemplate();

    PART_Content = GetTemplateChild("PART_Content") as ContentPresenter;
    if (PART_Content != null) {
        var binding = new Binding("AnimatedValue");
        binding.Source = this;
        PART_Content.SetBinding(ContentPresenter.ContentProperty, binding);
    }

}

Okay, now in theory it should work but I would advise reading the next section first and looking at the *actual* code I attached as opposed to the select snippets I put inline.

Okay, now it should work…

Really this is all that is needed to make the control functional. But it’s a little crude. The attached sample project is a bit more cleaned up than the inline code so far. There’s also additional functionality.

For one, the code above would use the default Double.ToString() conversion which means as the animation was in progress, you’d probably see a huge ugly number with lots of decimal places, no separators, and no currency symbols. The attached control adds a Format property and uses an included FormattingValueConverter. This way you can specify a format string as an attribute in the XAML to get the nice formatting as seen in the demo at the beginning of the article.

Also, the attached control does not create a new Storyboard everytime the value is animated. A single Storyboard is created in the constructor and is reused for the life of the control. The nice thing about this is that multiple changes to the value will “interrupt” the previous animation. So for example if it’s halfway through animation from 0 to 100 and you change it back to 0, it won’t continue to climb to 100 then drop to 0. It will stop climbing at 50 or whatever, and start dropping.

Finally, the attached control provides a TransitionDuration property that lets you control how long the control will spend transitioning. Right now the time is constant which is a little weird.

Using the code is pretty straightforward. The following is an example of a simple button that is bound to a TotalProfit value of the ViewModel and animates the value when it changes.

<Button Style="{StaticResource GlassButton}">
    <StackPanel>
        <TextBlock Text="Total Profit" />
        <Local:AnimatedNumber Value="{Binding TotalProfit}" Format="C2" />
    </StackPanel>
</Button>

Ideas for Additional Improvements

Some things that I didn’t have time to get to but would be nice:

  • Using an easing function to make the animation more fluid
  • Varying the duration of the animation based on how large or small the change is. For example if you changed 1000 to 1009, it’s kind of silly that the control spends 2 seconds making that transition.
  • Adding support for the Visual State Manager. In particular, it would be nice to have 3 states – Positive, Zero, Negative. This way you could color the value red when negative and green when positive without putting too much burden on the consumer of the control.

Please let me know what you think of this example. It’s my first time posting a lenthy example of a Silverlight control but as time permits I’d like to share more of my recent experiences.

Download Animated Number Project