A few days ago I was looking for a WPF slider that had three thumbs for a project I was working on. The three thumbs needed to be able to be customized individually and the central thumb needed to never exceed the values or locations of the left or right thumbs (i.e., think a video timeline with starting and ending points you can modify with the center thumb never exceeding these points). After searching for quite a while, all I could find was a slider with two thumbs so I modified it and decided to put it the modifications here to help out others who may need the same thing.
To break the code down a little bit there are three main sections, the actual slider component, the event handling of the three thumbs, and the invocation of the slider.
Slider Component
In the User Control xaml (ThreeThumbSlider.xaml), I added an additional slider thumb using the below code. This allows for the three thumbs to be rendered on the slider.
<Slider x:Name=”MiddleSlider” Minimum=”{Binding ElementName=root, Path=Minimum}” Maximum=”{Binding ElementName=root, Path=Maximum}” Value=”{Binding ElementName=root, Path=MiddleValue, Mode=TwoWay}” Template=”{StaticResource simpleSlider}” Margin=”0,0,0,0″/>
In addition, data binding and a dynamic resource allows for the color of each thumb to be modified. In the UserControl.Resources Track ControlTemplate I added the following:
<Rectangle Fill=”{DynamicResource ResourceKey=Clr}” Stroke=”Black” StrokeThickness=”1″ Width=”10″ Height=”18″ SnapsToDevicePixels=”True”/>
The following Resource Key was added within each of the thumbs:
<Slider x:Name=”MiddleSlider” Minimum=”{Binding ElementName=root, Path=Minimum}” Maximum=”{Binding ElementName=root, Path=Maximum}” Value=”{Binding ElementName=root, Path=MiddleValue, Mode=TwoWay}” Template=”{StaticResource simpleSlider}” Margin=”0,0,0,0″>
<Slider.Resources>
<Brush x:Key=”Clr”>Green</Brush>
</Slider.Resources>
</Slider>
Event Handling of the Three Thumbs
The event handling of the three thumbs is a bit tricky because you need to make sure that the middle slider does not exceed the values or location of the upper and lower valued sliders. If it does, the interaction can get a bit confusing. What I ended up doing was making three additional event handlers and checking for instances where the middle value exceeded the upper or lower thumb values using Math.Max() and Math.Min(). When this occurred, I reset the middle thumb value or the upper/lower values.
public event EventHandler LowerValueChanged;
public event EventHandler MiddleValueChanged;
public event EventHandler UpperValueChanged;
….
void Slider_Loaded(object sender, RoutedEventArgs e)
{
LowerSlider.ValueChanged += LowerSlider_ValueChanged;
UpperSlider.ValueChanged += UpperSlider_ValueChanged;
MiddleSlider.ValueChanged += MiddleSlider_ValueChanged;
}
public void LowerSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
MiddleSlider.Value = Math.Max(MiddleSlider.Value, LowerSlider.Value);
LowerValueChanged(this, e);
}
public void UpperSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
MiddleSlider.Value = Math.Min(MiddleSlider.Value, UpperSlider.Value);
UpperValueChanged(this, e);
}
public void MiddleSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
LowerSlider.Value = Math.Min(MiddleSlider.Value, LowerSlider.Value);
UpperSlider.Value = Math.Max(UpperSlider.Value, MiddleSlider.Value);
MiddleValueChanged(this, e);
}
To specify the thumb value for the middle slider, an additional variable and DependancyProperty were also added:
public double MiddleValue
{
get { return (double)GetValue(MiddleValueProperty); }
set { SetValue(MiddleValueProperty, value); }
}
public static readonly DependencyProperty MiddleValueProperty = DependencyProperty.Register(“MiddleValue”, typeof(double), typeof(ThreeThumbSlider), new UIPropertyMetadata(0d));
Invocation of the Slider
The following line can be added to any .xaml file (in my case the MainWindow.xaml) and allows for the specification of the lower, upper, and middle values of the thumbs as well as the handling of each thumb’s events:
<local:ThreeThumbSlider x:Name=”slider” LowerValue=”30″ UpperValue=”70″ MiddleValue=”40″ Minimum=”0″ Maximum=”100″ LowerValueChanged=”valueChange” UpperValueChanged=”valueChange” MiddleValueChanged=”valueChange” Canvas.Left=”81.955″ Canvas.Top=”34.811″ Width=”333.534″/>
Within the code behind of the MainWindow.xaml.cs, one event handler receives the changes to the thumbs and modifies the lower, middle, and upper labels using the code below. They are currently tied to a single event handler, but could easily be changed to individual event handlers or other functionality.
public void valueChange(object sender, EventArgs e)
{
lowerLabel.Text = slider.LowerValue.ToString();
upperLabel.Text = slider.UpperValue.ToString();
middleLabel.Text = slider.MiddleValue.ToString();
}