Posts tagged: Slider

Custom Slider Control – The Base Class

This is part 2 of a multi-part article on a custom Slider control.  You can find the other articles here:

The source code is now available on CodePlex

The purpose of this article is to start digging into some of the details behind the updated Slider control.  The best place to start is with the underlying base class.  The standard slider that comes with Silverlight has a base class called RangeBaseand can be found in the System.Windows.Controls.Primitives namespace.  This class defines the standard properties (Minimum, Maximum, LargeChange, SmallChange and Value).  Minimum and Maximum describe the lower and upper range of the slider itself will Value reflects the current value of the slider (where the thumb currently is located).  LargeChange and SmallChange indicate a value that will be used to increment/decrement the value property.  The RangeBase class also defines the ValueChanged event, which is fired when the Value property changes, and ensures all properties are correctly coerced (which I describe in more detail further down).

One of the main requirements for the new Slider was to be able to select a range of values (an upper and lower bound) rather than just a single value.  Since I still wanted to support everything else offered by RangeBase, I created DualRangeBase which inherits from it.  This became the base class for my version of the Slider control.

This new base class provides three additional properties:  LowerRangeValue, UpperRangeValue and  RangeValueLowerRangeValue and UpperRangeValueprovide the lower and upper values of the range (dictated by the position of the lower and upper thumbs repectively) while the RangeValue is the difference between the lower and upper values.  The DualRangeBase also adds three additional events:  LowerRangeValueChanged, UpperRangeValueChanged and RangeChanged.  The first two can be used if you want to handle the events separately while RangeChanged is fired any time either value changes.

The code snippet below shows the definitions of these properties.

// Defines the LowerRangeValue dependency property.
public static readonly DependencyProperty LowerRangeValueProperty = DependencyProperty.Register("LowerRangeValue", typeof(double), typeof(DualRangeBase), new PropertyMetadata(0.2d, OnLowerRangeValuePropertyChanged));

// Gets or sets the LowerRangeValue of the range.
public double LowerRangeValue
{
     get { return (double)GetValue(LowerRangeValueProperty); }
     set { SetValue(LowerRangeValueProperty, value); }
}

// Defines the UpperRangeValue dependency property.
public static readonly DependencyProperty UpperRangeValueProperty = DependencyProperty.Register("UpperRangeValue", typeof(double), typeof(DualRangeBase), new PropertyMetadata(0.8d, OnUpperRangeValuePropertyChanged));

// Gets or sets the UpperRangeValue of the range.
public double UpperRangeValue
{
     get { return (double)GetValue(UpperRangeValueProperty); }
     set { SetValue(UpperRangeValueProperty, value); }
}

///
/// Gets the size of the current range based on the difference
/// between the lower and upper range values.
///
public double RangeValue
{
    get { return UpperRangeValue - LowerRangeValue; }
}

The trickiest part of the base class, for me, was dealing with coercion.  I got the concept of this from the source code of the RangeBase class which uses coercion as well.  The concept of this is to ensure that when the value of any of the main properties (such as Maximum) is set it undergoes a set of validation checks.  If any of these checks fail the value is changed appropriately.  Coercion occurs during the properties PropertyChanged event handler.  For instance, the UpperRangeValue cannot be greater than the Maximum value.  If it is, the value is changed to equal Maximum.  You can imagine the trouble this can cause.  A single change to a value can cause a recursive loop of multiple changes.

This issue is at its worse when property values are being set during Design Time or the values are programmatically set within the code.  Let’s look at the previous example again and say that you want to set UpperRangeValue to 80.  Since we haven’t yet set Maximum to 100 (our target), the default value of 1 is used when the UpperRangeValue is coerced, which causes it to be set to 1 (the current value of Maximum).  Now you can see the difficulty I had ensuring the coercion between all the properties worked correctly to ensure the intended values got used.  I was able to overcome the issue (mentioned in the example) by coding the coercion process in a way that it remembered the intended value and attempts to use it when it can.  This means, in the example, after the Maximum is set to 100, the intended UpperRangeValue of 80 is applied.

In an attempt to understand this better, lets take a close look at the OnUpperRangeValuePropertyChanged method (which is called when the UpperRangeValue dependency property changes.

DualRangeBase dualRangeBase = d as DualRangeBase;

// Validate the provided UpperRangeValue.
if (!IsValidDoubleValue(e.NewValue))
{
    throw new ArgumentException("Invalid double value", UpperRangeValueProperty.ToString());
}

This beginning portion of the method simply creates an instance (since the method is static) of the DualRangeBase that called it and checks to ensure that the NewValue is valid.

// The code that follows is borrowed from the Microsoft code in RangeBase
// that performs the same actions on the Value property.  The trick here
// is to hold calls to the property changed methods until after all
// coercion has completed.
if (dualRangeBase.levelsFromUpperRootCall == 0)
{
    dualRangeBase.requestedUpperRangeValue = (double)e.NewValue;
    dualRangeBase.preCoersionUpperRangeValue = (double)e.NewValue;
    dualRangeBase.initialUpperRangeValue = (double)e.OldValue;
}
dualRangeBase.levelsFromUpperRootCall++;

This portion of the method saves the new value (requestedUpperRangeValue), the new value before it has been coerced (preCoersionUpperRangeValie) and the old value (initialUpperRangeValue).  These values are used later as part of the coercion testing.  If you remember from what I mentioned earlier, coercion can cause the value to be changed multiple times in an almost recursive manner.  levelsFromUpperRootCall is used to determine the current phase of this process.  I must thank Microsoft for this idea as it is how they did it and it works very well.

// Coerce values
dualRangeBase.CoerceUpperValue();

// This portion of the borrowed Microsoft code finally fires
// the change events once all coercion is confirmed complete.
dualRangeBase.levelsFromUpperRootCall--;
if (dualRangeBase.levelsFromUpperRootCall == 0)
{
    double value = dualRangeBase.UpperRangeValue;
    if (dualRangeBase.initialUpperRangeValue != value)
    {
        dualRangeBase.OnUpperRangeValueChanged(dualRangeBase.initialUpperRangeValue, value);
        dualRangeBase.OnRangeChanged(dualRangeBase.initialLowerRangeValue, dualRangeBase.LowerRangeValue, dualRangeBase.initialUpperRangeValue, value);
    }
}

The above code covers the last part of the method. First, the call to the coersion method is made. I talked about this a little bit already but the biggest thing to remember here is that the value being changed may actually be changed again during coersion. Once coersion has been completed, and the process has returned back to the initial call to the method, the OnUpperRangeValueChanged and OnRangeChanged events are fires.

That pretty much concludes the base class.  I know that might seem a bit confusing but you will understand my points if you step through the code once in Debug-mode.  All of the work that was put into the coercion functions allows the control to behave property when the properties are being set in the XAML in either Blend or the Visual Studio designer.  It also ensures only the appropriate values can be set.  I included a set of unit tests specifically for testing this and suggest you examine those in detail for further understanding.

Custom Slider Control – Overview

This is part 1 of a multi-part article on a custom Slider control. You can find the other articles here:

The source code is now available on CodePlex.

As I promised, here is the initial post in a series that I plan on writing that covers the Slider control that I created for Berico Tailored Systems (BTS).  This is the control that I entered into the Silverlight Control Contest and that I blogged about earlier.

 This custom Slider came about because of an application (SnagL) that I have been working on for Berico Tailored Systems (BTS).  Several of the application’s requirements dictated the need for a slider with additional functionality, primary the ability to provide a range of values rather than just a single value.  My initial plan was to simply subclass the existing Microsoft Slider control and make the required changes to include the additional Thumb control.  I also decided to add handles at either end of the slider to make the control more standardizes (as most sliders have these handles for increasing and decreasing the value of the slider).  I quickly found out that I couldn’t do this by inheriting from the existing Slider control.  The root of the problem is in the UpdateTrackLayout method.  The purpose of this method is to appropriately layout the controls along the track as the slider is updated.  Some of the calculations used in this method use ActualWidth and ActualHeight, which refers to the width and height of the control itself and not just the track that the Thumb controls are on.  Adding the handles to the either end of the track changes the overall size of the control which causes the calculations to be off.  Since this method is marked as internal, it cannot be overridden by our class.  I was forced to create the updated Slider control from scratch.

Since I was starting from scratch I figured I would design the control from the ground up and look at what is available to sliders in other applications.  I will say from the start that much of the code that was used for the Microsoft slider was reused for this control, although it was greatly altered.  Here are the main requirements I decided on:

  • Provide functionality to select a range
  • Provide a way to switch from a regular slider to a range slider
  • Provide an increase and decrease handle
  • Provide a way to turn handles on and off
  • Support all the standard slider functionality such as Orientation, IsDirectionReversed and IsEnabled
  • Support styling and custom styles and templates
  • Work with the Visual Studio IDE and Expression Blend
  • Display tooltips over the Thumbs during dragging

Below is a sample Silverlight application that demonstrates this new Slider control.  Obviously, you will need the Silverlight plugin installed to be able to see it.

Install Microsoft Silverlight


The two horizontal sliders have been fully restyled while the two vertical ones are using the default template. Ignoring the details on the styling, the code snippet below shows the XAML used to create all 4 of the sliders.

<Berico:Slider x:Name="TestH1" Style="{StaticResource FancySliderStyle}" IsEnabled="true" Grid.Column="1" Grid.Row="1" IsRangeEnabled="False" Value="5" />
<Berico:Slider x:Name="TestH2" Style="{StaticResource FancySliderStyle}" IsEnabled="true" Grid.Column="1" Grid.Row="2" IsRangeEnabled="True" />
<Berico:Slider x:Name="TestV1" IsEnabled="true" Grid.Column="2" Grid.Row="1" Grid.RowSpan="2" IsRangeEnabled="false" Value="5" Orientation="Vertical" />
<Berico:Slider x:Name="TestV2" IsEnabled="true" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2" IsRangeEnabled="True" LowerRangeValue="2" UpperRangeValue="8" Orientation="Vertical" />

I finally got around to setting up a CodePlex project for the custom controls (including this Slider) that I have been working on. I will update it as often as I can.

Silverlight Control Contest

I have been playing with Silverlight for a while now and recently came across a site that announced a contest for creating a custom Silverlight control.  I have always wanted to submit something  to a contest like this but never really had the time.  In this case, I already had a couple controls (a custom Slider and a GridSplitter) that I could use.  The catch is that I created this controls for Berico Tailored Systems (BTS), a company that I do some work on the side for.

BTS has a product called SnagL that is built on Silverlight 2.  I helped design the look of the application and created most of the controls that make up the user interface.  Marc Schweigert of Microsoft even conducted an interview with us about the product.  I have been very pleased with Silverlight and in how well some of the controls I worked on turned out.  While many of the controls are specific to SnagL, a few other controls are more general and usable by other applications so I broke those off into their own namespace.  This namespace contains the controls that I was curious about entering into the contest.

I decided, on a whim, to discuss the issue with BTS.  They were more then happy to embrace the idea and provided me permission to release these controls  as open source (under the Ms-PL license).  After some discussions and some research I got the official go ahead to release the source code as open source.

This leads me up to last night, when that final approval came through.  I selected the custom Slider control and created a new project to just include it.  I then submitted this control to the Silverlight Contest, which you can find here.  The contest ends on September 19th and I am excited about it.  Not only do I have a chance to win some great prizes but I am more than happy to share my custom Slider with the Silverlight community.

Aside from the contest, I hope to start a CodePlex project to host the custom control library.  I also plan on writing a series of detailed blogs on how I created each control (starting with the Slider).  Look for those soon.

I just wanted to update this article and mention that the winners for the control contest were announced and my Slider control was, unfortunately, not picked. I guess it just wasn’t flashy enough. I do hope  that it is, at the very least, helping someone else out in IT land.  I have started on that series of article that detail the Slider control and I have posted a project for it (and other custom controls) on CodePlex.

WordPress Themes