WPF Double to Fraction Converter

While on a project creating a WPF application for some real heavy Excel users, I’ve received a lot of requests to make it more like Excel.  One of the requirements that came in was to allow the users to type in fractions and view values as fractions in the WPF textboxes for values that were doubles in the model and in the database.

To fulfill this request, I needed a few things:

  • I needed to be able to find approximate fractional values for doubles.
  • I needed to validate and convert input in fractional format into a double.
  • I needed to output double values in a mixed fraction format.

Finding approximate fractional values for doubles

To handle this, I created a Fraction struct that has four properties.  IsPositive, WholeNumber, Numerator, and Denominator.  The meat of the struct is a static method called Parse that accepts a double and returns a Fraction.  It calls the private static method ApproximateFraction below:

/// <summary>
/// Approximates the provided value to a fraction.
/// </summary>
/// <param name="value">The double being approximated as a fraction.</param>
/// <param name="precision">Maximum difference targeted for the fraction to be considered equal to the value.</param>
/// <returns>Fraction struct representing the value.</returns>
private static Fraction ApproximateFraction(double value, double precision)
{
    bool positive = value > 0;
    int wholeNumber = 0; 
    int numerator = 1; 
    int denominator = 1;
    double fraction = numerator / denominator;

    while (System.Math.Abs(fraction - value) > precision)
    {
        if (fraction < value)
        {
            numerator++;
        }
        else
        {
            denominator++;
            numerator = (int)System.Math.Round(value * denominator);
        }

        fraction = numerator / (double)denominator;
    }

    if (numerator < 0) numerator = numerator * -1;

    while (numerator >= denominator)
    {
        wholeNumber += 1;
        numerator -= denominator;
    }

    return new Fraction(positive, wholeNumber, numerator, denominator);
}

Validate and convert fractional formatted inputs into doubles

This part actually ended up being the ConvertBack method in the IValueConverter created to bind to TextBoxes.  The basic idea here was to first try and parse the value as a double.  If it failed, I checked to see if it matched my regular expression for a mixed fraction.  If it did, I parsed that mixed fraction out and made a Fraction struct.  If not, I throw a FormatException.

/// <summary>
/// Converts a string into a nullable double
/// </summary>
/// <param name="value">A string value that should be transformable into a double</param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    if (value != null && !string.IsNullOrEmpty(value.ToString()))
    {
        double rValue;
        string rawValue = value.ToString().Trim();
        rawValue = rawValue.Replace("- ", "-");
        while(rawValue.Contains("  "))
        {
            rawValue = rawValue.Replace("  ", " ");
        }

        // Regular Expression that represents a number in Fraction format.
        Regex FractionRegex = new Regex(@"^-?([0-9]* )?[0-9]+/[0-9]+$");

        // If the value can be parsed as a double, do it and return
        if (double.TryParse(rawValue, out rValue))
        {
            return rValue;
        }
        // Else if the value can be read as a fractional value, extract the number and return the a double from it. 
        else if (FractionRegex.IsMatch(rawValue))
        {
            // Check to see if the input 
            if (FractionRegex.IsMatch(rawValue))
            {
                try
                {
                    Regex numeratorRegex = new Regex(@"(s|^|-)[0-9]+/");
                    bool isNegative;
                    int wholeNumber;
                    int numerator;
                    int denominator;

                    isNegative = rawValue.StartsWith("-");
                    wholeNumber = Math.Abs(rawValue.Any(x => x == ' ') ? int.Parse(rawValue.Remove(rawValue.IndexOf(" "))) : 0);
                    denominator = int.Parse(rawValue.Substring(rawValue.LastIndexOf("/") + 1));
                    numerator = Math.Abs(int.Parse((numeratorRegex.Match(rawValue)).Value.Replace("/", "")));

                    return new Fraction(!isNegative, wholeNumber, numerator, denominator).ToDouble();
                }
                catch
                {
                    throw new FormatException(String.Format("Invalid Format:  {0} cannot be converted to a numeric value.", value.ToString()));
                }
            }
        }
        //  This value could not be parsed as a double and didn't match a fraction using our Fractional Regular Expression, throw a FormatException.
        else
        {
            throw new FormatException(String.Format("Invalid Format:  {0} cannot be converted to a numeric value.", value.ToString()));
        }
    }

    return null;
}

Output double values in a mixed fraction format

An finally the Convert method of the IValueConverter used to display doubles bound to string properties such as TextBox.Text or TextBlock.Text.  In this method I have a couple of things going on, but first things first, I change the double into an instance of the Fraction struct.  Using the ConverterParameter, I allow the developer to choose whether or not they want to restrict the use of fractional output to certain denominators.  There are four possible return paths:

  1. Null, in the case the nullable double is null.
  2. Mixed fraction format when denominators are being restricted.
  3. The decimal format when denominators are being restricted and the fraction’s denominator isn’t in the allowed list.
  4. Mixed fraction format when denominators aren’t being restricted.
/// <summary>
/// Converts a double into a string.
/// </summary>
/// <param name="value">A nullable double to get converted into a string</param>
/// <param name="targetType"></param>
/// <param name="parameter">true or false, determines if we use the denominator restrictions</param>
/// <param name="culture"></param>
/// <returns>A string representing the double value, maybe in decimal format, maybe in fractional format.</returns>
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    string decimalFormatString = "0.######";
    bool restrictDenominator = false;

    if (parameter != null)
    {
        if (!bool.TryParse(parameter.ToString(), out restrictDenominator))
        {
            restrictDenominator = false;
        }
    }

    double? dValue = value as Nullable<double>;
    if (restrictDenominator && dValue != null && dValue.HasValue)
    {
        Fraction asFraction = Fraction.Parse(dValue.Value);
        var validDenominators = new List<int>(new int[] { 2,3,4,5,6,7,8,9,16,32,64 });
        if (validDenominators.Contains(asFraction.Denominator))
        {
            return asFraction.ToString(validDenominators, decimalFormatString);
        }
        else
        {
            return dValue.Value.ToString(decimalFormatString);
        }
    }
    else if (!dValue.HasValue)
    {
        return dValue;
    }
    else
    {
        Fraction asFraction = Fraction.Parse(dValue.Value);
        return asFraction.ToString(null, String.Empty);
    }
}

Using the Converter in your WPF Application

The last thing of course would be to use the converter in your application.  If you haven’t used converters in WPF yet, it is fairly easy.  First you need to declare an XML namespace pointing at the .NET namespace where your converters live (see the line in the code sample below that starts “xmlns:converter”).  Next you need to declare a resource for your specific converter (See the one line in “Window.Resources”).  Finally you create a binding using the converter which is done below in the TextBox Text property.

<Window x:Class="Andora.BlogSample.FractionTextBoxes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converter="clr-namespace:Andora.BlogSample.FractionTextBoxes.Converters"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <converter:FractionConverter x:Key="fractionConverter" />
    </Window.Resources>
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel>
            <Label Content="Enter a numeric in decimal or fraction format: " />
            <TextBox Text="{Binding MyDouble, Converter={StaticResource ResourceKey=fractionConverter}, ConverterParameter=true}" Height="25"  />

Conclusion

If you are developing in an environment where the users are accustomed to viewing and entering data in fraction formats, without too much hassle, you can provide users of your WPF applications the ability to do exactly that.  You can find a full working sample with code all zipped up using the link below.  Happy coding!!

Sample Project Download

WPF Date Range Double Slider Control (Part 1)

As I mentioned in my introduction to this series, I’m going over various aspects of how I created a DateTime Range slider control.  In this article, I’m going to be going over the basics of creating the user control.  Source for this project can be found on CodePlex.

First, to get two thumbs onto the slider, I overrode the control template of the regular slider with a template called “simpleSlider” to get rid of the track of each slider as well as some other default pieces of the base control template such as the repeater buttons.  With these things there, one or the other of the sliders would sit on top of the other and really just mess with my users’ experience.

Slider Control Template
  1. <ControlTemplate x:Key=“simpleSlider” TargetType=”{x:Type Slider}>
  2.     <Border SnapsToDevicePixels=“true” BorderBrush=”{TemplateBinding BorderBrush} BorderThickness=”{TemplateBinding BorderThickness}>
  3.         <Grid>
  4.             <Grid.RowDefinitions>
  5.                 <RowDefinition Height=“Auto”/>
  6.                 <RowDefinition Height=“Auto” MinHeight=”{TemplateBinding MinHeight}/>
  7.                 <RowDefinition Height=“Auto”/>
  8.             </Grid.RowDefinitions>
  9.  
  10.             <Rectangle x:Name=“PART_SelectionRange”/>
  11.  
  12.             <Track x:Name=“PART_Track” Grid.Row=“1”>
  13.                 <Track.Thumb>
  14.                     <Thumb x:Name=“Thumb” Style=”{StaticResource ResourceKey=HorizontalSliderThumbStyle} />
  15.                 </Track.Thumb>
  16.             </Track>
  17.         </Grid>
  18.     </Border>
  19. </ControlTemplate>

 

Then my control is made with the two sliders that use the “simpleSlider” template and a single Border control to be my track for the two sliders:

DateTime Range Slider
  1. <Grid VerticalAlignment=“Center” Background=“Transparent”>
  2.     <Border BorderThickness=“0,1,0,0” BorderBrush=“DarkGray” VerticalAlignment=“Bottom” Height=“1” HorizontalAlignment=“Stretch”
  3.            Margin=“0,0,0,10”/>
  4.  
  5.     <Slider x:Name=“LowerSlider” VerticalAlignment=“Top” IsEnabled=”{Binding ElementName=root, Path=IsLowerSliderEnabled, Mode=TwoWay}
  6.            Minimum=”{Binding ElementName=root, Path=Minimum, Converter={StaticResource ResourceKey=dtdConverter}}
  7.            Maximum=”{Binding ElementName=root, Path=Maximum, Converter={StaticResource ResourceKey=dtdConverter}}
  8.            Value=”{Binding ElementName=root, Path=LowerValue, Mode=OneWay, Converter={StaticResource ResourceKey=dtdConverter}}
  9.            Template=”{StaticResource simpleSlider}
  10.            Margin=“0,0,10,0”
  11.            SmallChange=”{Binding ElementName=root, Path=SmallChange, Converter={StaticResource ResourceKey=timespanToDoubleConverter}}
  12.            LargeChange=”{Binding ElementName=root, Path=LargeChange, Converter={StaticResource ResourceKey=timespanToDoubleConverter}}
  13.             />
  14.  
  15.     <Slider x:Name=“UpperSlider” IsEnabled=”{Binding ElementName=root, Path=IsUpperSliderEnabled, Mode=TwoWay}
  16.            Minimum=”{Binding ElementName=root, Path=Minimum, Converter={StaticResource ResourceKey=dtdConverter}}
  17.            Maximum=”{Binding ElementName=root, Path=Maximum, Converter={StaticResource ResourceKey=dtdConverter}}
  18.            Value=”{Binding ElementName=root, Path=UpperValue, Mode=OneWay, Converter={StaticResource ResourceKey=dtdConverter}}
  19.            Template=”{StaticResource simpleSlider}
  20.            Margin=“10,0,0,0”
  21.            SmallChange=”{Binding ElementName=root, Path=SmallChange, Converter={StaticResource ResourceKey=timespanToDoubleConverter}}
  22.            LargeChange=”{Binding ElementName=root, Path=LargeChange, Converter={StaticResource ResourceKey=timespanToDoubleConverter}}
  23.             />
  24. </Grid>

 

Mainly because I didn’t want to spend the time (at this point) dealing with making this a slider that could handle more than just a DateTime range, the Minimum, Maximum, and Value properties of the sliders are using an IValueConverter to handle the conversion between the doubles the sliders expose to DateTime values.  The SmallChange and LargeChange properties are converted from TimeSpans my control expects to doubles that the slider controls expect.

DateTime to Double Converter
  1. public class DateTimeDoubleConverter : IValueConverter
  2. {
  3.     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  4.     {
  5.         DateTime dt = DateTime.Parse(value.ToString());
  6.         return dt.Ticks;
  7.     }
  8.  
  9.     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  10.     {
  11.         double d = double.Parse(value.ToString());
  12.         return new DateTime((long)d);
  13.     }
  14. }
TimeSpan to Double Converter
  1. public class TimeSpanToDoubleConverter : IValueConverter
  2. {
  3.     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  4.     {
  5.         TimeSpan givenValue = (TimeSpan)value;
  6.         return givenValue.Ticks;
  7.     }
  8.  
  9.     public object ConvertBack(object value, Type targetType,
  10.        object parameter, System.Globalization.CultureInfo culture)
  11.     {
  12.         return new TimeSpan(((long)value));
  13.     }
  14. }

WPF Date Range Double Slider Control (Introduction)

For a project I’m working on, I needed a way to select a date range.  One of my first thoughts was to use a double slider only to find that one didn’t exist with the OOTB controls so I decided to roll my own.   In order to solidify things in my mind as well as help anyone out there looking for resources on various aspects of development in WPF, I thought I’d write about my experience creating this control.

I’ll be adding blog entries in the next week or two to describe how I accomplished implemenation of some of the features I wanted in my control:

  • Create a two-thumbed slider control to choose a DateTime range. (Part 1)
  • Don’t let the the lower slider or upper slider pass each other.  I was creating this to use with a graph and if the sliders passed each other even for a millisecond, the graph would throw an exception. (Part 2)
  • Dependency Properties  (Part 3)

I did lose some of the functionality of the regular slider in my first version of this control, such as clicking to the right or left of the thumb to change the value and displaying the slider in a vertical format, but overall, I’m happy with this control as it is so far (and so was my client.)

Because I wanted to continue work on this control and make it easy to download and use the code, the source for the control and a sample project are available at CodePlex.