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

Posted in C#, WPF | Tagged , , | Leave a comment

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

Posted in C#, WPF, XAML | 6 Comments

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

Posted in WPF, XAML | Tagged | 4 Comments

WPF Textbox Select All on Focus

A while back I was creating an application that needed to allow fast and accurate input.  In WPF, the default behavior of the TextBox on focus is to put the cursor where it was the last time the TextBox had lost focus or if it hasn’t had focus yet, at the beginning.  The users of the application wanted this to be changed so that when the TextBox got focus, all current text was selected.  I found various ways to do this, but am putting this blog post together to get everything I ended up doing all in one place for easy access.

Basic Focus – GotFocus Event vs. GotKeyboardFocus Event

There is a decision to be made here.  In the application that had me working on this stuff for the first time, I chose the GotKeyboardFocus Event.  The difference is really that GotFocus will fire when your TextBox gets focus from within the application (or Window), but not when your Window/Application regains focus.  GotKeyboardFocus will fire anytime your TextBox gains or regains keyboard focus.   So if you are working with something like AvalonDock and have various sections (i.e. DockableContent) that accept input and want your TextBoxes to select all when each section regains focus use the GotKeyboardFocus event.  If your application is one where users will be multi-tasking a lot causing your window to constantly lose and regain focus and you don’t want all of the text to get selected each time they come back to your application, you’d use the GotFocus event.

Sample Handler:

GotFocus or GotKeyboardFocus
  1. private void TextBox_SelectAllText(object sender, RoutedEventArgs e)
  2. {
  3.     ((TextBox)sender).SelectAll();
  4. }

 

Mouse Focus:  PreviewMouseDown Event

Just using GotFocus or GotKeyboardFocus doesn’t give us what we want when someone clicks into the TextBox.  You’ll see it quickly select all, then deselect and put the cursor where you clicked.  Using the PreviewMouseDown event, you can check if the TextBox already has Keyboard focus and if it doesn’t, select everything.  The example below also checks for a triple click…and selects all if it is a triple click.

PreviewMouseDown Event Handler
  1. /// <summary>
  2. /// Handles PreviewMouseDown Event.  Selects all on Triple click.  
  3. /// If SelectAllOnEnter is true, when the textbox is clicked and doesn't already have keyboard focus, selects all
  4. /// </summary>
  5. /// <param name="sender"></param>
  6. /// <param name="e"></param>
  7. private void TextBox_SelectivelyIgnoreMouseButton(object sender, MouseButtonEventArgs e)
  8. {
  9.     // If its a triple click, select all text for the user.
  10.     if (e.ClickCount == 3)
  11.     {
  12.         TextBox_SelectAllText(sender, new RoutedEventArgs());
  13.         return;
  14.     }
  15.  
  16.     // Find the TextBox
  17.     DependencyObject parent = e.OriginalSource as UIElement;
  18.     while (parent != null && !(parent is TextBox))
  19.     {
  20.         parent = System.Windows.Media.VisualTreeHelper.GetParent(parent);
  21.     }
  22.  
  23.     if (parent != null)
  24.     {
  25.         if (parent is TextBox)
  26.         {
  27.             var textBox = (TextBox)parent;
  28.             if (!textBox.IsKeyboardFocusWithin)
  29.             {
  30.                 // If the text box is not yet focussed, give it the focus and
  31.                 // stop further processing of this click event.
  32.                 textBox.Focus();
  33.                 e.Handled = true;
  34.             }
  35.         }
  36.     }
  37. }

 

Application-Wide Event Handlers

Now of course, in every Window, User Control, View, etc., you don’t want to have to add these event handlers to each and every TextBox.  To easily and quickly attach these handlers to every TextBox within your application you can use the EventManager class to register these event handlers to the TextBox class application-wide.  I put these in the event handler for the Application.Startup event.

Application.Startup Handler
  1. private void Application_Startup(object sender, StartupEventArgs e)
  2. {
  3.     EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotFocusEvent, new RoutedEventHandler(TextBox_SelectAllText));
  4.     EventManager.RegisterClassHandler(typeof(TextBox), TextBox.PreviewMouseDownEvent, new MouseButtonEventHandler(TextBox_SelectivelyIgnoreMouseButton));
  5. }

Posted in C#, WPF | Tagged | 8 Comments

WPF MarkupExtension Class

This week I was going through StackOverflow, looking for anything I could help with and cam across someone asking about how to bind to an attribute of a class member in XAML.  I wrote a small example IValueConverter class they could use to accomplish the task which was the only way I knew of that it could be done.  Then, another poster (H.B.) solved the task using something I’d never heard of before…the MarkupExtension class.

Using the example H.B. gave, creating a class to display the DisplayName attribute of a property, you can see that this approach gets the job done just as well as a converter, but to me, it looks like it solves the problem in a much more direct way than a converter.  The converters are generally used to convert a value from one Type to another Type, for instance, from a boolean to a value of System.Windows.Visibility or from a Color to a Brush.

To create a MarkupExtension class, you derive from the abstract MarkupExtension class.  There is one method to override, called ProvideValue.  A default constructor is required unless you intend to only support attribute usages of the class.  If you want to support arguments in the usage, might also need to define additional constructors to match the settable properties.

Attached is a sample WPF application with code that implements the MarkupExtension and IValueConverters I’ve mentioned in this short write-up.

Sample Code

Posted in C#, WPF, XAML | Tagged | Leave a comment

Using Style Triggers to Give ToolTips to DataGrid Columns

Working with a DataGrid on a recent project, I had to get ToolTips on all of the DataGrid column headers.  At first go around, I had used the first thing that came to my head which was to insert a TextBlock into my Header and give it a ToolTip.

TextBlock in Header
  1. <DataGridTemplateColumn.Header>
  2.     <TextBlock Text="Column B" ToolTip="A short explanation of Column B"/>
  3. </DataGridTemplateColumn.Header>

 

This worked OK for a while, I got my expected result which was a ToolTip when a user moused over the header…although it wasn’t exactly what I wanted because they had to mouse over the text of my header, but I was OK with that.

Later on, another requirement came in, to allow the users to choose which columns were visible and which were hidden and that is when the flaw in my first approach was exposed (more on this later) and I found an easier way using a trigger to display tool tips and didn’t require creating a TextBlock in DataGridTemplateColumn.Header. 

ToolTip from Style Trigger
  1. <Style TargetType="{x:Type DataGridColumnHeader}">
  2.     <Style.Triggers>
  3.         <Trigger Property="IsMouseOver" Value="True">
  4.             <Setter Property="ToolTip" Value="{Binding Column.(ToolTipService.ToolTip), RelativeSource={RelativeSource Self}}"/>
  5.         </Trigger>
  6.     </Style.Triggers>
  7. </Style>
  8. <!– Use ToolTipService.ToolTip to assign the tooltip value –>
  9. <DataGridTemplateColumn x:Name="colA" Width="40*" IsReadOnly="True" Header="Column A" ToolTipService.ToolTip="Explanation of Column A">

Posted in WPF, XAML | Tagged , , | 2 Comments

Choosing foreground using Luminosity Contrast Ratio

Sample Source Code

When a background color is chosen by the user, you can use a converter that calculates luminosity contrast ratio to choose the best foreground color for the background given.  In the code sample attached, I use a converter that does this getting the luminosity contrast for the background with a white foreground and a black foreground.  Whichever has the better luminosity contrast ratio, is the foreground color I use.

I got formula for the luminosity contrast ratio from the W3C Recommendation for Web Content Accessiblity Guidelines (WCAG) 2.0.  Outside of code, the formula looks like:

(L1 + 0.05) / (L2 + 0.05), where

  • L1 is the relative luminance of the lighter color
  • L2 is the relative luminance of the darker color

Next question I had was “how do I calculate relative luminance”?  The formula for this is also within the guidelines.  First of all, a definition:  relative luminance is “the relative brightness of any point in a colorspace, normalized to 0 for the darkest black and 1 for the lightest white.”

It can be calculated using the formula below:

Relative Luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B, where:

R:  If RsRGB <= 0.03928 then R = RsRGB  / 12.92 else R = ((RsRGB  + 0.055)/1.055)2.4
G:  If GsRGB <= 0.03928 then G = GsRGB  / 12.92 else G = ((GsRGB  + 0.055)/1.055)2.4
B:  If BsRGB <= 0.03928 then B = BsRGB  / 12.92 else B = ((BsRGB  + 0.055)/1.055)2.4

Ok, but another variable is introduced here, how are RsRGB, GsRGB, BsRGB each calculated?  This is a bit easier and is defined in the same area as the relative luminance formula.

  • RsRGB = R8bit/255
  • GsRGB = G8bit/255
  • BsRGB = B8bit/255

Finally, now how does all of this look in C#?

Calculate Luminosity Contrast
  1. private double LuminosityContrast(Color foreground, Color background)
  2. {
  3.     var foregroundLuminosity = RelativeLuminance(foreground.R, foreground.G, foreground.B);
  4.     var backgroundLuminosity = RelativeLuminance(background.R, background.G, background.B);
  5.  
  6.     if (foregroundLuminosity > backgroundLuminosity)
  7.     {
  8.         return (foregroundLuminosity + 0.05) / (backgroundLuminosity + 0.05);
  9.     }
  10.     else
  11.     {
  12.         return (backgroundLuminosity + 0.05) / (foregroundLuminosity + 0.05);
  13.     }
  14. }
  15.  
  16. private double RelativeLuminance(byte r, byte g, byte b)
  17. {
  18.     double rs = ((double)r / 255);
  19.     double gs = ((double)g / 255);
  20.     double bs = ((double)b / 255);
  21.     double R = 0;
  22.     double G = 0;
  23.     double B = 0;
  24.  
  25.     R = (rs <= 0.03928) ? (double)(rs / 12.92) : Math.Pow(((rs + 0.055) / 1.055), 2.4);
  26.     G = (gs <= 0.03928) ? (double)(gs / 12.92) : Math.Pow(((gs + 0.055) / 1.055), 2.4);
  27.     B = (bs <= 0.03928) ? (double)(bs / 12.92) : Math.Pow(((bs + 0.055) / 1.055), 2.4);
  28.  
  29.     return ((double)(0.2126 * R)) + ((double)(0.7152 * G)) + ((double)(0.0722 * B));
  30. }

 

And now, I can use this in my Convert method of my IValueConverter:

IValueConverter – Convert
  1. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  2. {
  3.     if (value != null && value is Color)
  4.     {
  5.         Color backgroundColor = (Color)value;
  6.  
  7.         // Calculate the luminosity constrast ratio for the given color against black and white.
  8.         var blackContrast = LuminosityContrast(Colors.Black, backgroundColor);
  9.         var whiteContrast = LuminosityContrast(Colors.White, backgroundColor);
  10.  
  11.         // return a solid color brush using either black or white, depending on which had the higher luminosity constrast ratio.
  12.         return blackContrast >= whiteContrast ? new SolidColorBrush(Colors.Black) : new SolidColorBrush(Colors.White);
  13.     }
  14.     else
  15.     {
  16.         return new SolidColorBrush(Colors.Black);
  17.     }
  18. }

 

Finally, I can (declare and) use my converter in XAML to pick the better color for use against the background the user of my application has chosen.

Using the converter
  1. <Converters:ForegroundColorFromBackgroundColorConverter x:Key="foregroundDetector" />
  2. <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
  3.            Foreground="{Binding SelectedBrush.Color, Converter={StaticResource ResourceKey=foregroundDetector}, Mode=OneWay}">
  4.     <Run FontWeight="Bold">Selected Color</Run>
  5. </TextBlock>

Posted in C#, WPF | Tagged , | 5 Comments

WPF Minimal Button Styling

With all of the blog posts about Button styling in WPF since its release, it amazes me that there is very little out there about totally removing the Button default styling.  I needed an image to be my button, but without overriding the control template for the button, you can’t remove the borders or get rid of the bluish mouse over effects.  Below is a style for a borderless button, whose mouse over and IsPressed effect is a small size transformation to the content. 

Borderless Button Style
  1. <Style x:Key="BorderlessButton" TargetType="{x:Type Button}">
  2.     <Setter Property="Padding" Value="1"/>
  3.     <Setter Property="Background" Value="Transparent" />
  4.     <Setter Property="Template">
  5.         <Setter.Value>
  6.             <ControlTemplate TargetType="{x:Type Button}">
  7.                 <Border Name="border" Background="{TemplateBinding Background}">
  8.                     <ContentPresenter Name="content" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
  9.                                       Margin="{TemplateBinding Padding}"
  10.                                       RecognizesAccessKey="True"
  11.                                       SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
  12.                                       VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  13.                 </Border>
  14.                 <ControlTemplate.Triggers>
  15.                     <Trigger Property="IsMouseOver" Value="True">
  16.                         <Setter TargetName="content" Property="RenderTransform" >
  17.                             <Setter.Value>
  18.                                 <ScaleTransform ScaleX="1.1" ScaleY="1.1" />
  19.                             </Setter.Value>
  20.                         </Setter>
  21.                     </Trigger>
  22.                     <Trigger Property="IsPressed" Value="True">
  23.                         <Setter TargetName="content" Property="RenderTransform" >
  24.                             <Setter.Value>
  25.                                 <ScaleTransform ScaleX=".95" ScaleY=".95" />
  26.                             </Setter.Value>
  27.                         </Setter>
  28.                     </Trigger>
  29.                     <Trigger Property="IsFocused" Value="True">
  30.                     </Trigger>
  31.                 </ControlTemplate.Triggers>
  32.             </ControlTemplate>
  33.         </Setter.Value>
  34.     </Setter>
  35. </Style>

Posted in WPF | Tagged , | 5 Comments

WPF Date Range Double Slider Control (Part 3–Dependency Properties)

This is part 3 in a series I’m writing about a WPF User Control I’ve created and posted up on CodePlex.  In this installment, I’m going to be talking about creating dependency properties, in the DateRangeSlider, I used them to enable/disable the upper and lower sliders independently for dealing with the maximum, minimum, upper and lower values.

First of all, a little bit about dependency properties.  A dependency property is a type of property introduced with WPF that solves many problems in WPF around styling, data binding, animation, and more.  As I’ve been working with WPF, I’ve noticed that one of the primary features of a dependency property used is its built-in ability to provide change notification.  If you start diving into MVVM, you might start asking yourself why do I need a dependency property instead of a property that utilizes the INotifyPropertyChanged implementation?  That is when the styling and animating come into play, because you can only style and animate dependency properties.

If you look through the code at CodePlex, you’ll see I have dependency properties for the slider’s minimum, maximum, upper value, lower value, enabling the lower slider, enabling the upper slider, and finally locking the lower and upper values to the minimum and maximum respectively.   We’ll take a look at the Minimum property. 

I want to start with Line 8 below, because this is where the real definition of the MinimumProperty is located.  It is a public static field and by convention, has a Property suffix.  Depedency properties are created using the DependencyProperty.Register method which at the minimum takes needs the name of the dependency property to register (“Minimum”) and the type of the property (typeof(DateTime)) and the type of the owner (typeof(DateRangeSlider)) that is registering the dependency property.  The overload of Register I used also includes a PropertyMetadata argument where I defined a default value and a callback method.

Line 2 starts the basic property definition. with your get and set methods that call the methods GetValue and SetValue which are inherited by System.Windows.DependencyObject.  Each dependency property you make will look almost identical to this because there is no guarantee that your getter or setter will be called directly, WPF will usually call the GetValue and SetValue methods directly, bypassing any custom logic you might feel compelled to add.

The last thing you’ll find in the snippet below is the callback method.  If you need custom logic to be run when your property is changed, you’ll want to use an overload of the DependencyProperty.Register method that lets to define a callback method in the PropertyMetadata argument.  In my example, I’m using the callback to check if the LowerValue should be locked to the Minimum or if the LowerValue is less than the new Minimum and adjusting it accordingly.  One last thing to note here, is that our callback is static, which means it isn’t connected to a particular instance.  The instance we want to work with is inside the argument DependencyObject d and we must first cast it out.

Dependency Property – Minimum
  1. #region Dependency Property – Minimum
  2. public DateTime Minimum
  3. {
  4.     get { return (DateTime)GetValue(MinimumProperty); }
  5.     set { SetValue(MinimumProperty, value); }
  6. }
  7.  
  8. public static readonly DependencyProperty MinimumProperty =
  9.     DependencyProperty.Register("Minimum", typeof(DateTime), typeof(DateRangeSlider), new UIPropertyMetadata(DateTime.Now.AddDays(-15), new PropertyChangedCallback(OnMinimumChanged)));
  10.  
  11. public static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  12. {
  13.     DateRangeSlider slider = (DateRangeSlider)d;
  14.  
  15.     if (slider.IsLowerValueLockedToMin || slider.LowerValue < (DateTime)e.NewValue)
  16.     {
  17.         slider.LowerValue = (DateTime)e.NewValue;
  18.     }
  19. }
  20.  
  21. #endregion

Hopefully, this helps explain dependency properties to anyone looking for it, I’ve never claimed to be a great writer (or even a mediocre one).  If I missed anything or if you have questions, let me know.

Posted in C#, WPF | Tagged , | 3 Comments

WPF Date Range Double Slider Control (Part 2)

As I mentioned in my introduction to this series, I’m going over various aspects of how I implemented a control that allows a user to choose a DateTime range using a double slider control.  In Part One, I covered the basics of what I did to the control templates, put the slider together and showed the IValueConverters I used to handle the conversions from doubles to DateTime and doubles to TimeSpans.  In this article, I’ll be talking about what I did to keep the upper value and the lower value sliders from crossing each other.  The source for the control and a sample project can be found at CodePlex

My first thought doing this was to play it simple and bind the lower slider’s Maximum to the upper slider’s current value and the and the upper sliders Minimum to the lower’s current value.   While this seemed logical to me, when I implemented it, it put the two sliders on two totally separate scales and it failed miserably.  Not only were the sliders able to cross, since the scales were constantly changed, while I would move the lower slider, the upper slider would be moving (and not changing value).

My second attempt was much better, but still didn’t work as expected.  In this attempt, I subscribed to the ValueChanged event for each of the sliders.  In these event handlers, I had a piece of code as shown in “Attempt 2:  Fail” that was setting the slider controls values so they wouldn’t cross each other (for long) and then setting dependency properties (UpperValue and LowerValue) to those upper and lower values.  This failed because I wasn’t setting the upper slider’s value to something greater than the lower slider’s value until after this code ran.  To a user, this would be acceptable, you can’t actually see the values cross.  However, I was using this to help zoom in and out of a charting control  and once they crossed, even for a few milliseconds, the charting control would blow up on me.  It was quite messy…line fragments, points, and data everywhere.

Attempt 2: Fail
  1. private void LowerSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
  2. {
  3.     UpperSlider.Value = Math.Max(UpperSlider.Value, LowerSlider.Value + oneSecond);
  4.     LowerValue = new DateTime((long)LowerSlider.Value);
  5.     UpperValue = new DateTime((long)UpperSlider.Value);
  6. }

 

So, finally, my 3rd attempt I hit gold.  Why was I setting LowerValue and UpperValue be set by the sliders when I was also setting them in the ValueChanged events?  So I changed my bindings in the sliders to OneWay bindings so I have complete control of the range values and the sliders can suggest was my new UpperValue and LowerValue should be, but I get the final decision in my event handlers.  In my event handlers, I check if the slider values have passed each other and set the dependency properties to appropriate values that make sure that the UpperValue is greater than the LowerValue.

Slider Control Template
  1. private void UpperSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
  2. {
  3.     LowerSlider.Value = Math.Max(Math.Min(UpperSlider.Value oneSecond, LowerSlider.Value), Minimum.Ticks);
  4.  
  5.     var _upperValue = new DateTime((long)UpperSlider.Value);
  6.     var _lowerValue = new DateTime((long)LowerSlider.Value);
  7.  
  8.     if (UpperValue > _lowerValue)
  9.     {
  10.         LowerValue = _lowerValue;
  11.         UpperValue = _upperValue;
  12.     }
  13.     else
  14.     {
  15.         UpperValue = _upperValue;
  16.         LowerValue = _lowerValue;
  17.     }
  18. }

Posted in C#, WPF | Tagged , , | 2 Comments