Download MathEvalConverter.zip

Background

With the name Einstein, people typically assume I’m good at math. I have the utmost respect for the physicists and mathemeticians of our time, prior, and beyond. But to be honest, math is not my strong suit. In fact I have great difficulty simply adding or subtracting numbers without the use of a calculator. This deficiency actually worked to my advantage last month when I was observing the users of an application I had recently dogfed.

The application had several numeric input fields that took in costs, prices, commissions, etc. Like any run of the mill business application, these inputs affected various calculations that updated the UI accordingly. But while observing the users I kept noticing a very peculiar behavior. When they would type into a box for Cost, Price, etc. they would whip out a small desk calculator and add up some numbers before typing them into the box.

As it turns out, the values I took for granted as a pre-calculated input often require various "in-your-head" steps before arriving at what I assumed was a known "input".

"The cost of the product is normally $15 but I knocked it down by $3."

Given my mathematical challenges, I was very sympathetic to this situation. I don’t own a desk calculator but I am very familiar with the Start -> Run -> calc.exe ceremony. If I happen to have a PowerShell window open I’ll use that instead.

Did I miss a key requirement? Should there have been more input fields? Not really because the calculation steps are not necessarily formal aspects of the system. They’re mostly things people might normally do in their head had they not happen to have a calculator in front of them. And the type of calculations they perform vary from case to case.

Then I remembered… I encounter this same situation all the time when working in Excel. But Excel’s primary feature is the fact that you can type a "formula" or expression anywhere you’d put a constant value. Wouldn’t it be great if the text boxes in my application offered the same functionality? Well by the end of this post, they will!

The Plan

The idea is that my input boxes would allow the user to enter a number as usual, but if they entered an expression such as "2+2", committing the value would enter "4" into the field. The expression doesn’t need to be preserved, so the value of the field can still be backed by a simple number. Also, I only need to implement very basic expressions. I’ll stick to addition, subtraction, multiplication, division, grouping, and any combination of the above.

I thought about where I might add this functionality. I could subclass TextBox, but then the cells in a DataGrid would need to be addressed separately. I could probably have done it with a behavior but that didn’t seem appropriate either. In the end I decided to implement an IValueConverter. I was already using one to provide currency formatting in the control.

The end result would look like the working example shown below.

The Math Parser

Because this application was an in-browser Silverlight application, I could avoid having to write my own math parser by taking advantage of the HTML bridge and the Eval() method. In short, I would pass the expression to the JavaScript engine to evaluate as if it were a line of code. Of course, I would need to validate the input first to ensure a malicious user could not take advantage of this fact.

To isolate this shortcut into replacable component, I extracted an IMathEvaluator interface that would be defined as follows.

public interface IMathEvaluator {
    bool TryEvaluate(string expression, out decimal value);
}

This allows me to include my JavaScript hack for the sake of this post while admitting that a less lazy developer could substitute a better implementation. One nice thing about this implementation is that I don’t need to determine if the user entered a number or expression. Even if they entered a simple number like 1, I still treat it as an expression.

The implementation is pretty simple too. It simply checks the input against a Regex to ensure it’s not going to pass some malicious payload to the JavaScript engine then calls Eval() via the Silverlight HTML bridge.

public sealed class JScriptMathEvaluator : IMathEvaluator
{
    public bool TryEvaluate(string expression, out decimal value ) {

        // When there's no text, just return zero
        if ( expression == null || expression.Trim().Length == 0 ) {
            value = 0;
            return true;
        }
        
        // remove currency symbols and commas
        // these are added when we format the number but we want to remove them before
        // parsing the text value because it would invalidate the JavaScript syntax
        var numberFormat = CultureInfo.CurrentCulture.NumberFormat;
        expression = expression.Replace( numberFormat.CurrencySymbol, "" );
        expression = expression.Replace( numberFormat.CurrencyGroupSeparator, "" );
        expression = expression.Replace( numberFormat.NumberGroupSeparator, "" );
        expression = expression.Replace( numberFormat.PercentGroupSeparator, "" );

        // Ensure that a simple expression consisting of only digits,
        // parenthases, and four operators (+, -, *, /) are entered.
        // Never pass non-validated input to JavaScript!
        if (Regex.IsMatch(expression, @"^[0-9\.\+\-\*\/\(\)\s]+$" ) ) {

            try {
                object eval = System.Windows.Browser.HtmlPage.Window.Eval( expression );
                value = Convert.ToDecimal(eval);
                return true;
            }   // try
            catch ( Exception ex ) {
                // trace exception or whatever
                // but let the function return false
            }   // catch

        }   // if

        value = 0;
        return false;

    }
}

The Value Converter

In order to convert the user’s input into a number I’ll need to invoke a conversion process when the value of the TextBox changes. I was already doing this with a FormattingConverter that would apply currency formatting to the number and then parse the text value with NumberStyles.

So let’s just derive a class from FormattingConverter with support for the ConvertBack method. We’ll call it MathEvalConverter.

public sealed class MathEvalConverter : FormattingConverter
{

    public MathEvalConverter( )
    {
        MathEvaluator = new JScriptMathEvaluator( );
    }

    public IMathEvaluator MathEvaluator
    {
        get;
        set;
    }

    protected override object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
    {

        // View -> ViewModel

        string expression = System.Convert.ToString( value );

        decimal numberValue;
        if ( MathEvaluator != null && MathEvaluator.TryEvaluate( expression, out numberValue ) ) {
            return numberValue;
        }   // if

        // We couldn't parse the expression.
        // Let silverlight reject it because:
        //   - if we throw an exception, it Silverlight bypasses validation events
        //   - if we return DependencyProperty.UnsetValue, the input will just disappear with no error!
        // By returning the unconverted value back to Silverlight, at least they'll see a conversion error.

        return base.ConvertBack( value, targetType, parameter, culture );

    }

}   // class

The converter doesn’t specifically use the JavaScript implementation. It goes through the IMathEvaluator interface which happens to be implemented by JScriptMathEvaluator.

MathEvalConverter In Action

We can apply the MathEvalConverter to any two-way binding between a numeric property on the binding source and a string property on a control. Since it derives from FormattingConverter, we can supply a format string to pretty format the numbers.

<UserControl>
    <FrameworkElement.Resources>
        <Local:MathEvalConverter x:Key="Eval" Format="C2" />
        <Local:FormattingConverter x:Key="Date" Format="d" />
    </FrameworkElement.Resources>
    <Form:DataForm Header="Expense Report">
        <StackPanel>

            <Form:DataField Label="Reported By">
                <TextBox Text="{Binding Name, Mode=TwoWay}" />
            </Form:DataField>

            <Form:DataField Label="Expense Date">
                <TextBox Text="{Binding Date, Converter={StaticResource Date}, Mode=TwoWay}" />
            </Form:DataField>

            <Form:DataField Label="Meals">
                <TextBox Text="{Binding Meals, Converter={StaticResource Eval}, Mode=TwoWay}" />
            </Form:DataField>

        </StackPanel>
    </Form:DataForm>
</UserControl>

Summary

Enough talk. Download the code, and let me know what you think. I have no idea if anyone finds these posts useful if you don’t post comments!

Download MathEvalConverter.zip

These aren’t your typical, obvious tips. Head over to the Visual Studio Blog to learn some WPF performance tips that the team picked up during the development of Visual Studio 2010. Some of the performance issues required changes to WPF that we all will benefit from in .NET 4. Others are clever ways to work around various idiosyncracies of WPF.

Take this for example.

Optimize for Remote Desktop scenarios : Use scrolling hint

Also on the topic of Remote Desktop, one area where we needed additional support from WPF was for scrolling in the text editor. As I mentioned above, when in a remote session, all WPF content is transmitted as a bitmap. When the text editor scrolls by a line, that means that the entire contents of the editor region needs to be retransmitted as a bitmap. This, of course can be expensive – the larger the area of the text view, the larger the bitmap and the slower it will be. Fortunately, WPF 4.0 now knows how to issue a “ScrollWindow” command to the remote desktop session which drastically reduces the amount of information transferred across the wire. Only the scroll operation itself (very short) and the newly-exposed line need to be transmitted. To take advantage of this new operation, you need to use the property VisualScrollableAreaClip. There are some restrictions on where this can be used, so read the documentation carefully.

Feb 272010

Download LINQ.psm1

I’m gonna start off by saying the module I’m posting about does not follow the PowerShell naming guidelines. In fact, not only does it use non-standard verb names, but the "verb" part of all the function names aren’t even verbs. I hate breaking the rules like this but the Verb-Noun naming couldn’t really group these functions together the way I wanted and naming them correctly would have been very difficult. So feel free to rename as needed.

A while back I posted about a ConvertTo-Dictionary PowerShell function. This handy little function would take an input object from the pipeline and populate a Hashtable using a key selector and optional value selector. Very similar to how LINQ’s ToDictionary method works. It uses ScriptBlocks in the same way C# uses lambda expressions. There is one very nice trick in the module. A function called Invoke-ScriptBlock that shows how to inject a $_ (dollar underbar) variable into a ScriptBlock’s scope when invoking it. This is very handy for simulating lambdas in PowerShell functions.

Anyhow, over time I added several more LINQ-like functions and then I just went all out and tried to implement all the most popular LINQ methods. I managed to get a good deal done although there is still some documentation work needed. Some of the functions may seem redundant because they have nearly the same functionality as existing PowerShell cmdlets. In these cases it’s usually because I thought it could be more LINQ-like. For example I have Linq-Min because it’s simpler and more flexible than Measure-Object. But I didn’t duplicate Where-Object because there was no point.

The following code example shows how you might use these functions. Not all of them are shown. Notice the lamba-like syntax for predicates, selectors, etc. They also make use of the convenient $_ (dollar underbar) variable to represent the "current item" which makes them more consistent with Where-Object, Foreach-Object, etc.

Import-Module LINQ

function Assert-AreEqual($Expected, $Actual) {
    if (@(Compare-Object $Expected $Actual -SyncWindow 0).Length) {
        $OFS = ','
        Write-Error "Assert-AreEqual Failed: Expected=($Expected), Actual=($Actual)"
    }
}

# All
Assert-AreEqual -Expected ($true)   -Actual (1..5 | Linq-All { $_ -gt 0 })
Assert-AreEqual -Expected ($false)  -Actual (1..5 | Linq-All { $_ -gt 6 })
Assert-AreEqual -Expected ($true)   -Actual (@()  | Linq-All { $_ -gt 6 })

# Any
Assert-AreEqual -Expected ($true)   -Actual (1..5 | Linq-Any)
Assert-AreEqual -Expected ($false)  -Actual (@()  | Linq-Any)
Assert-AreEqual -Expected ($true)   -Actual (1..5 | Linq-Any { $_ -eq 3 })
Assert-AreEqual -Expected ($false)  -Actual (1..5 | Linq-Any { $_ -eq 6 })

# First
Assert-AreEqual -Expected @(1)      -Actual @(1..5 | Linq-First)
Assert-AreEqual -Expected @(2)      -Actual @(1..5 | Linq-First { $_ -gt 1 })
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-First { $_ -gt 5 })

# Last
Assert-AreEqual -Expected @(5)      -Actual @(1..5 | Linq-Last)
Assert-AreEqual -Expected @(4)      -Actual @(1..5 | Linq-Last { $_ -lt 5 })
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Last { $_ -lt 1 })

# Single
Assert-AreEqual -Expected @(2)      -Actual @(1..5 | Linq-Single { $_ -eq 2 })
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Single { $_ -gt 5 })

# Skip
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Skip 0)
Assert-AreEqual -Expected @(3..5)   -Actual @(1..5 | Linq-Skip 2)
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Skip 5)
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Skip 6)

# SkipWhile
Assert-AreEqual -Expected @(2..5)   -Actual @(1..5 | Linq-SkipWhile { $_ -eq 1})
Assert-AreEqual -Expected @(3..5)   -Actual @(1..5 | Linq-SkipWhile { $_ -lt 3})
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-SkipWhile { $false })
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-SkipWhile { $true })

# Take
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Take 5)
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Take 6)
Assert-AreEqual -Expected @(1..5)   -Actual @(1..6 | Linq-Take 5)
Assert-AreEqual -Expected @(1..3)   -Actual @(1..5 | Linq-Take 3)
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Take 0)

# TakeWhile
Assert-AreEqual -Expected @(1..2)   -Actual @(1..5 | Linq-TakeWhile { $_ -lt 3})
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-TakeWhile { $_ -eq 2})
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-TakeWhile { $false })
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-TakeWhile { $true })

# Distinct
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Distinct)
Assert-AreEqual -Expected @(1..5)   -Actual @(1,2,3,4,5,1,2,3,4,5 | Linq-Distinct)
Assert-AreEqual -Expected @()       -Actual @(Linq-Distinct)

# Repeat
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Repeat 1)
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Repeat 0)
Assert-AreEqual -Expected @(1,1,2,2,3,3,4,4,5,5) -Actual @(1..5 | Linq-Repeat 2)

# Except
Assert-AreEqual -Expected @(1,3,5)  -Actual @(1..5 | Linq-Except (2,4))
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Except 6)
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Except (1..5))

# Intersect
Assert-AreEqual -Expected @(2,4)    -Actual @(1..5 | Linq-Intersect (2,4))
Assert-AreEqual -Expected @()       -Actual @(1..5 | Linq-Intersect 6)
Assert-AreEqual -Expected @(1..5)   -Actual @(1..5 | Linq-Intersect (1..5))

# IndexOf
Assert-AreEqual -Expected (0)       -Actual (1..5 | Linq-IndexOf { $_ -eq 1 })
Assert-AreEqual -Expected (3)       -Actual (1..5 | Linq-IndexOf { $_ -eq 4 })
Assert-AreEqual -Expected (-1)      -Actual (1..5 | Linq-IndexOf { $_ -eq 6 })
Assert-AreEqual -Expected (2)       -Actual (1,2,3,3,3 | Linq-IndexOf { $_ -eq 3 })

# Count
Assert-AreEqual -Expected (5)       -Actual (1..5 | Linq-Count)
Assert-AreEqual -Expected (1)       -Actual (1..5 | Linq-Count { $_ -eq 3 })
Assert-AreEqual -Expected (0)       -Actual (1..5 | Linq-Count { $_ -eq 6 })

# Average, Min, Max
Assert-AreEqual -Expected (3)       -Actual (1..5 | Linq-Average)
Assert-AreEqual -Expected (15)      -Actual (1..5 | Linq-Sum)
Assert-AreEqual -Expected (5)       -Actual (1..5 | Linq-Max)
Assert-AreEqual -Expected (1)       -Actual (1..5 | Linq-Min)

The complete function listing is shown below. I’ve been selfishly keeping this module to myself for about a year now and I felt it was time to post it. Let me know what you think!

Name Synopsis
Linq-All Determines whether all elements of a sequence satisfy a condition.
Linq-Any Determines whether any element of a sequence satisfies a condition.
Linq-Average Returns the average of values in a sequence.
Linq-Count Returns a number that represents how many elements in the specified sequence satisfy a condition.
Linq-Distinct Returns unique items from the pipeline input.
Linq-Except Excludes from the pipeline input the items which also appear in a second set.
Linq-Expand Drills into an input object based on one or more property names to simplify access to data inside nested structures.
Linq-First Returns the first element in a sequence that satisfies a specified condition.
Linq-IndexOf Returns the zero-based position of the first element in a sequence that meets the specified criteria.
Linq-Intersect Includes from the pipeline input only the items that exist in a second set.
Linq-Last Returns the last element of a sequence that satisfies a specified condition.
Linq-Max Returns the maximum value in a sequence.
Linq-Min Returns the minimum value in a sequence.
Linq-Repeat Repeats the items from the pipeline a specified number of times.
Linq-Select Selects a property, property set, or ScriptBlock projection of the input.
Linq-SelectMany Similar to Linq-Select but always ensures the output is wrapped in an array.
Linq-Single Returns a single item that satisfies a specified condition, and throws an exception if more than one or zero such element exists.
Linq-Skip Skips the specified number of items from the input and then returns the remaining items.
Linq-SkipWhile Skips items from the input as long as a specified condition is true and then returns the remaining items.
Linq-Sum Returns the sum of values in a sequence.
Linq-Take Returns a specified number of contiguous items from the start of the input pipeline.
Linq-TakeWhile Returns items from the input as long as a specified condition is true.
Linq-ToDictionary Creates a Hashtable from a sequence according to specified key selector and value selector functions.
Linq-ToSet Creates a HashSet containing only unique items from a sequence.
Where-Like Returns items from a sequence whose selected values match one or more wildcard patterns.
Where-Match Returns items from a sequence whose selected values match one or more regular expression patterns.

Download LINQ.psm1

I recently blogged about a new method I noticed in .NET 4 on the Enum class. One of the great things about .NET 4 is that it is a new version of base class library too. In .NET 3.0 and 3.5 as we all know, the CLR and BCL was left at version 2.0 which meant there were practically no improvements to core system classes and such.

Well I just stumbled upon another welcome upgrade to a rusty old class. Path.Combine in .NET 4 takes a parameter array of parts.

// so code that used to look like this...
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string filename =
    Path.Combine(documents,
        Path.Combine("Visual Studio 2008",
            Path.Combine("Projects", "My Project");

// now looks like this...
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string filename = Path.Combine(documents, "Visual Studio 2008", "Projects", "MyProjects");

H3Viewer

Update: There was a bug in H3Viewer at the time I posted this that showed an empty TOC and Index on x64 versions of Windows. Rob Chandler just informed me the current build (1.0.0.20) fixes this and I have confirmed it now works fine on x64! Apparently in between Beta 2 and RC Microsoft may have changed the help agent from 32 bit to 64 bit which relocated some registry keys. H3Viewer is still a 32 bit application but works fine on 64 bit Windows.

I hate Visual Studio 2010’s new help system. Well hate isn’t really the right word. It’s more like loathing with every fiber of my being. There was a pretty good discussion going on in the comments of Brian Harry’s blog on MSDN. The bottom line is though, it’s here to stay.

Fortunately a Microsoft Help MVP named Robert Chandler has created H3Viewer which is an interface on top of the new help system that aims to bring back the familiar feel of dexplore, including a TOC, index, and bookmarks. It doesn’t change the fact that help is now served by a local web server but it’s a great improvement over the out of box experience. Sorry but IE/Chrome are not good help viewers.

Download FullScreenDemo Project

So now I’m working on an application that will be used at a trade show in a booth. Customers will come up to the application and type in the area code and exchange of their phone number and the application will tell them whether or not they can move their phone number over to us. Piece of cake. But obviously a battleship gray Windows Forms application will simply not do. I chose WPF for the project because it needs to access an offline database, interact with an external COM server (MapPoint), and run full screen with keyboard input.

That last point is what led me to the code I posted tonight. Running a WPF application in full screen mode is pretty trivial.

  • Set WindowStyle = None
  • Set TopMost = True
  • Set WindowState = Maximized

But coming off a recent Silverlight project, I was a little perturbed about the fact that Silverlight can easily “go full screen” and back whereas in WPF it felt really manual. Plus there were a few other improvements I wanted to make that I could easily wrap up into a reusable behavior. For one, using a full-screen window during debugging is a pain in the ass, even with two monitors. Therefore, I want to be able to toggle full screen mode at runtime.

Here are a few common ways that applications enter and exit full screen mode. This behavior supports them all.

  • Many full-screen apps allow the user to toggle full screen mode by double clicking inside the window.
  • Many full-screen apps allow the user to exit full screen mode by pressing escape.
  • Many full-screen apps implicitly enter full screen mode when the window is maximized.

The only one that was tricky was the last one. Since WPF does not have a “StateChanging” event, what was happening was the Window (with its border and title bar) is maximized before the event is raised. By that time, setting the WindowStyle property to remove the caption and border didn’t affect the maximized size so the maximized window was still showing the taskbar. It took a little trial and error but I settled on using WM_SYSCOMMAND.

Usage of the behavior is simple.

<Window ... >

    <i:Interaction.Behaviors>

        <Einstein:FullScreenBehavior
                FullScreenOnDoubleClick="True"
                FullScreenOnMaximize="True"
                RestoreOnEscape="True" />

    </i:Interaction.Behaviors>

</Window>

The attached demo includes the FullScreenBehavior.cs which has no external dependencies. Enjoy.

Full Screen Demo

Shh… hear that? It’s the sound of a million developers ripping out their home-grown HasFlag, IsFlagSet, CheckFlag, etc helper methods. Just noticed that in .NET 4 System.Enum now has a built-in HasFlag method. As Forrest Gump would say “Lt. Dan says we don’t have to worry about enum flags no more. That’s good. One less thing.”

I like unit testing. I’m not a hard liner so I don’t take it too seriously. I don’t necessarily fully subscribe to the test-first approach in all cases and I think aiming for 100% code coverage is rarely practical. But I do like unit tests. They give my a certain peace of mind and set a minimum standard for functionality.

But I just ran into a bug in my code that I didn’t catch in my unit tests but the end user caught pretty early on. Typing a decimal value into a field that was bound to an Int64 property on the viewmodel didn’t produce any error but simply reverted the field to zero. As the developer, it never occurred to me that someone would want to put a non-integer value in the field. I even guarded against negative integers.

Anyhow, the fix for now is “don’t type fractional minutes in the minutes field.” The bug is not particularly disruptive, just confusing. The nature of the field is such that a fractional minute is meaningless, but the end user is entering numbers directly off a bill which happens to have way more precision than is needed. It’s super low priority so I’ll queue it up for when other bugs need to be fixed. But I thought it was a great example of how developers can know the code inside and out, yet the end user will always find a bug you never even considered.

It’s a simple one, but useful. And it can clean up some of your ugly scripts that have a lot of redundant paths embedded in strings.

# Reduced for the sake of the example
# The full advanced function is included at the end.
function Use-Location($Path, $Body) {

    Push-Location $Path

    try { &$Body }
    finally { Pop-Location }

}

Usage of the function couldn’t be simpler and uses a C#-style scope syntax.

Use-Location "C:\Temp" {

    mkdir textfiles
    dir *.txt | move textfiles

    mkdir images
    dir *.jpg,*.png | move images

}

Obviously you could just as well use Push-Location and Pop-Location in a try/catch yourself but why? This way enforces clean scoping and ensures you don’t forget the call to pop.

##############################################################################
#.SYNOPSIS
# Executes a ScriptBlock within the context of the specified working directory.
#
#.DESCRIPTION
# This function sets the current location to the specified Path and executes a
# ScriptBlock. All relative paths in the ScriptBlock will be based on the new
# current directory. When the ScriptBlock exits, the old current directory
# will be restored.
#
#.PARAMETER Path
# The working directory to use for the duration of the ScriptBlock.
#
#.PARAMETER ScriptBlock
# The code to execute within the context of the new current directory.
#
#.PARAMETER Create
# Creates the directory if it does not exist.
#
#.EXAMPLE
# Use-Location "C:\Temp" {
#
#    mkdir textfiles
#    dir *.txt | move textfiles
#
#    mkdir images
#    dir *.jpg,*.png | move images
#
# }
#
#.LINK 
# Push-Location
# Pop-Location
##############################################################################
function Use-Location {

    [CmdletBinding()]
    param(
        
        [Alias('PSPath')]
        [Alias('LiteralPath')]
        [Parameter(Position=1, Mandatory=$true)]
        [String]$Path,
        
        [Parameter(Position=2, Mandatory=$true)]
        [ScriptBlock]$ScriptBlock,
        
        [Parameter()]
        [Switch]$Create
        
    )

    $ErrorActionPreference = 'Stop'

    # Create if necessary
    if ($Create) {
        $Exists = Test-Path -LiteralPath $Path -PathType Container
        if (-not $Exists) {
            New-Item $Path -ItemType Directory | Out-Null
        }
    }

    Push-Location $Path -StackName 'Use-Location'

    try { &$ScriptBlock }
    finally { Pop-Location -StackName 'Use-Location' }

}

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