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

WPF 4 DataGrid: Getting the Row Number into the RowHeader

After struggling way too long on this, I posted the question to StackOverflow and of course, received a fairly quick answer (thanks Meleak!).  Since I couldn’t find any good solutions out there with Google & Bing, I thought I’d post it here for the next person struggling with this.

What I was trying to do was get the row number into the RowHeader of the DataGrid, so it has an Excel-ish column to let my users see which record in the set they were looking at.  The solution I’ve seen out there on the web suggests adding an index field to the business objects. This wasn’t really an option for me at this point because the DataGrid will be getting resorted a lot and we don’t want to have to keep track of changing these index fields constantly just to show row numbers on the grid.

The solution required a smidgen of code-behind which some developers frown upon, but this is solely for the way the grid looks so I was much more comfortable keeping this with the UI logic as opposed to throwing extraneous fields on my business objects to help with how the UI looks.

  1. <!– DataGrid XAML –>
  2. <DataGrid LoadingRow="DataGrid_LoadingRow"
  3.  
  4.     ———————
  5.  
  6. // Code Behind
  7. private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
  8. {
  9.     e.Row.Header = (e.Row.GetIndex() + 1).ToString();
  10. }

 

The result, a DataGrid with a row header for my rows that specifies the row number…that doesn’t get changed as my grid is sorted and resorted and resorted.

DataGridRowHeaders

Posted in WPF | Tagged | 12 Comments

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. }

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

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.

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

Image Source Converter

Each time I have the need for an image converter for a WPF application, I end up searching the web looking for a sample, so this time, I’m putting a sample somewhere I can find it again easily.  Below is a basic IValueConverter implementation to convert a file name into a BitmapImage located in an images folder.

Code for Sample Project

public class IconImageConverter : IValueConverter
{
    public object Convert(object value, 
                       Type targetType, 
                       object parameter, 
                       System.Globalization.CultureInfo culture)
   {
      // if value isn't null, we can safely do the conversion. 
      if (value != null)
      {
          string imageName = value.ToString();
          Uri uri = new Uri(String.Format("/Images/{0}", imageName), 
                                UriKind.Relative);
          return new BitmapImage(uri);
      }
      return null;
   }
    public object ConvertBack(object value, 
                         Type targetType, 
                         object parameter, 
                         System.Globalization.CultureInfo culture)
    {
       throw new NotImplementedException();
    }
}
Aside | Posted on by | Tagged , | 1 Comment