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

Filtering Items in a WPF ComboBox

I was building a WPF application for a client which had a few ComboBoxes with large amounts of items as options in them.  The client asked for the users to have the ability to filter the items in the drop down, but still force them to choose an item in the list.  A search of the internet yielded some solutions, but each solution required me to set the ComboBox.IsEditable property to “True”.  In each solution I found and tried doing this always made the user experience a little weird, allowing them to type in whatever they wanted, but still only allowing them to choose an item within the list.

UnFiltered-FilteringComboBox

FilteringComboBox

What I did to finally solve the problem was actually pretty simple and I was surprised none of the solutions I came across didn’t go this route.  I created a new UserControl to handle this implementation so it would be easier to reuse throughout the application and others in the future. 

The first part of it was creating a new Style for the ComboBox, so that inside the Popup (PART_Popup) portion of the ControlTemplate can include my filtering TextBox.

<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
    <Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
        <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
            <Grid RenderOptions.ClearTypeHint="Enabled">
                <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                    <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
                </Canvas>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <DockPanel LastChildFill="True" Margin="0,5,10,5" Grid.Row="0">
                        <TextBlock DockPanel.Dock="Left" Text="Filter:" Margin="0,0,5,0" />
                        <TextBox x:Name="DropDownFilterTextBox" />
                    </DockPanel>
                    <ScrollViewer x:Name="DropDownScrollViewer" Grid.Row="1">
                        <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </ScrollViewer>
                </Grid>
            </Grid>

        </Border>
    </Microsoft_Windows_Themes:SystemDropShadowChrome>
</Popup>

The other piece of this is filtering the items in the within the ComboBox as they type in the filter TextBox.  To do this, I attach to the TextChanged event on it.

protected void DropDownFilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    TextBox textBox = ((TextBox)sender);

    ComboBox cbo = ((TextBox)sender).TemplatedParent as ComboBox;
    cbo.Items.Filter += a =>
    {
        if (a.ToString().ToLower().StartsWith(textBox.Text.ToLower()))
        {
            return true;
        }
        return false;
    };
}

Get the VS 2010 Samples Solution for this blog which includes the WPF ComboBox with an items Filter

WPF DataGrid, Read-Only Row

A common requirement for a DataGrid is to have entire rows that are read-only.  You may have a list of records in your DataGrid and some need to be locked for various reasons.  Unfortunately, in the XAML definition of a DataGrid, there isn’t a way to mark a row as being Read-Only.  To accomplish this without tying my view to my model or rewriting the DataGrid, I’m utilizing the BeginningEdit event, the Tag property of the DataGridRow, and created a style for the DataGridRow with a DataTrigger to put everything together.

First thing I did was to make sure my Model had an IsReadOnly property to latch onto, in the attached sample code, I did this by adding the property to one of the Customer in the Northwind database . 

CustomerReadOnlyProperty

Next, using a Trigger in my DataGridRow style, if the data item’s IsReadOnly property evaluates to “True” then I set the DataGridRow ‘s Tag to a string I can look for in my BeginningEdit event handler.  In the sample project attached and in the code snippets below, I used the string “ReadOnly”.  For a visual cue, I also set the HeaderTemplate to show an image to indicate that the row is locked. 

XAML Styles
  1. <!– Row Header Template to show a Locked icon.–>
  2. <DataTemplate x:Key="rowHeaderTemplate">
  3.     <StackPanel Orientation="Horizontal">
  4.         <Image x:Name="editImage" Source="Images/lock.gif" Width="16" Margin="1,0" />
  5.     </StackPanel>
  6. </DataTemplate>
  7. <!– DataGridRow Style –>
  8. <Style x:Key="ReadOnlyCheck" TargetType="{x:Type DataGridRow}">
  9.     <Style.Triggers>
  10.         <DataTrigger Binding="{Binding IsReadOnly}" Value="True">
  11.             <Setter Property="Tag" Value="ReadOnly" />
  12.             <Setter Property="HeaderTemplate" Value="{StaticResource rowHeaderTemplate}">
  13.             </Setter>
  14.         </DataTrigger>
  15.     </Style.Triggers>
  16. </Style>

 

CustomerReadOnlyVisual

Finally, in my BeginningEdit event handler for my DataGrid, I look at the Row going into Edit Mode, if its Tag property has been set to “ReadOnly” I cancel the event so it never makes it into edit mode.  This enforces the IsReadOnly property from my mode.

BeginningEdit Event Handler
  1. /// <summary>
  2. /// Event Handler for BeginningEdit Event of DataGrid.  If the Row entering into Edit Mode has a Tag set to "ReadOnly",
  3. /// cancel the edit.
  4. /// </summary>
  5. /// <param name="sender">Reference to the DataGrid</param>
  6. /// <param name="e">Instance of DataGridBeginningEditEventArgs</param>
  7. private void dataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
  8. {
  9.     if (((DataGridRow)e.Row).Tag != null && ((DataGridRow)e.Row).Tag.ToString() == "ReadOnly")
  10.     {
  11.         e.Cancel = true;
  12.     }
  13. }

 

Sample Code Download