/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.Windows.Controls; using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Media; namespace Xceed.Wpf.Toolkit { [TemplatePart( Name = PART_LowerRange, Type = typeof( RepeatButton ) )] [TemplatePart( Name = PART_HigherRange, Type = typeof( RepeatButton ) )] [TemplatePart( Name = PART_HigherSlider, Type = typeof( Slider ) )] [TemplatePart( Name = PART_LowerSlider, Type = typeof( Slider ) )] [TemplatePart( Name = PART_Track, Type = typeof( Track ) )] public class RangeSlider : Control { #region Members private const String PART_LowerRange = "PART_LowerRange"; private const String PART_Range = "PART_Range"; private const String PART_HigherRange = "PART_HigherRange"; private const String PART_HigherSlider = "PART_HigherSlider"; private const String PART_LowerSlider = "PART_LowerSlider"; private const String PART_Track = "PART_Track"; private RepeatButton _lowerRange; private RepeatButton _higherRange; private Slider _lowerSlider; private Slider _higherSlider; private Track _lowerTrack; private Track _higherTrack; private double _deferredUpdateValue; #endregion Members #region Constructors static RangeSlider() { DefaultStyleKeyProperty.OverrideMetadata( typeof( RangeSlider ), new FrameworkPropertyMetadata( typeof( RangeSlider ) ) ); } public RangeSlider() { this.SizeChanged += this.RangeSlider_SizeChanged; } #endregion Constructors #region Properties #region AutoToolTipPlacement public static readonly DependencyProperty AutoToolTipPlacementProperty = DependencyProperty.Register( "AutoToolTipPlacement", typeof( AutoToolTipPlacement ), typeof( RangeSlider ), new FrameworkPropertyMetadata( AutoToolTipPlacement.None, RangeSlider.OnAutoToolTipPlacementChanged ) ); public AutoToolTipPlacement AutoToolTipPlacement { get { return (AutoToolTipPlacement)GetValue( RangeSlider.AutoToolTipPlacementProperty ); } set { SetValue( RangeSlider.AutoToolTipPlacementProperty, value ); } } private static void OnAutoToolTipPlacementChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { var rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnAutoToolTipPlacementChanged( (AutoToolTipPlacement)e.OldValue, (AutoToolTipPlacement)e.NewValue ); } } protected virtual void OnAutoToolTipPlacementChanged( AutoToolTipPlacement oldValue, AutoToolTipPlacement newValue ) { } #endregion //AutoToolTipPlacement #region AutoToolTipPrecision public static readonly DependencyProperty AutoToolTipPrecisionProperty = DependencyProperty.Register( "AutoToolTipPrecision", typeof( int ), typeof( RangeSlider ), new FrameworkPropertyMetadata( 0 ) ); public int AutoToolTipPrecision { get { return (int)GetValue( RangeSlider.AutoToolTipPrecisionProperty ); } set { SetValue( RangeSlider.AutoToolTipPrecisionProperty, value ); } } #endregion //AutoToolTipPrecision #region HigherRangeBackground /// /// Get/Set the Brush for the Range between Higher and Maximum values. (Brush) /// public static readonly DependencyProperty HigherRangeBackgroundProperty = DependencyProperty.Register( "HigherRangeBackground", typeof( Brush ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( Brushes.Transparent ) ); public Brush HigherRangeBackground { get { return (Brush)GetValue( RangeSlider.HigherRangeBackgroundProperty ); } set { SetValue( RangeSlider.HigherRangeBackgroundProperty, value ); } } #endregion HigherRangeBackground #region HigherRangeStyle /// /// Get/Set the Style for the Range between Higher and Maximum values. (Style) /// public static readonly DependencyProperty HigherRangeStyleProperty = DependencyProperty.Register( "HigherRangeStyle", typeof( Style ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( null ) ); public Style HigherRangeStyle { get { return (Style)this.GetValue( RangeSlider.HigherRangeStyleProperty ); } set { this.SetValue( RangeSlider.HigherRangeStyleProperty, value ); } } #endregion HigherRangeStyle #region HigherRangeWidth /// /// HigherRangeWidth property is a readonly property, used to calculate the percentage of the HigherRange within the entire min/max range. /// /// private static readonly DependencyPropertyKey HigherRangeWidthPropertyKey = DependencyProperty.RegisterAttachedReadOnly( "HigherRangeWidth", typeof( double ) , typeof( RangeSlider ), new PropertyMetadata( 0d ) ); public static readonly DependencyProperty HigherRangeWidthProperty = HigherRangeWidthPropertyKey.DependencyProperty; public double HigherRangeWidth { get { return (double)GetValue( RangeSlider.HigherRangeWidthProperty ); } private set { SetValue( RangeSlider.HigherRangeWidthPropertyKey, value ); } } #endregion HigherRangeWidth #region HigherThumbBackground /// /// Get/Set the Brush for the HigherValue thumb's background [active state]. (Brush) /// public static readonly DependencyProperty HigherThumbBackgroundProperty = DependencyProperty.Register( "HigherThumbBackground", typeof( Brush ), typeof( RangeSlider ) ); public Brush HigherThumbBackground { get { return (Brush)GetValue( RangeSlider.HigherThumbBackgroundProperty ); } set { SetValue( RangeSlider.HigherThumbBackgroundProperty, value ); } } #endregion HigherThumbBackground #region HigherValue /// /// HigherValue property represents the higher value within the selected range. /// public static readonly DependencyProperty HigherValueProperty = DependencyProperty.Register( "HigherValue", typeof( double ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( 0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, RangeSlider.OnHigherValueChanged, RangeSlider.OnCoerceHigherValueChanged ) ); public double HigherValue { get { return (double)GetValue( RangeSlider.HigherValueProperty ); } set { SetValue( RangeSlider.HigherValueProperty, value ); } } private static object OnCoerceHigherValueChanged( DependencyObject d, object basevalue ) { var rangeSlider = (RangeSlider)d; if( (rangeSlider == null) || !rangeSlider.IsLoaded) return basevalue; var min = Math.Min( rangeSlider.Minimum, rangeSlider.Maximum ); var max = Math.Max( rangeSlider.Minimum, rangeSlider.Maximum ); var higherValue = Math.Max( rangeSlider.Minimum, Math.Min( rangeSlider.Maximum, (double)basevalue ) ); higherValue = Math.Max( rangeSlider.LowerValue, ( double)basevalue ); return higherValue; } private static void OnHigherValueChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args ) { RangeSlider rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnHigherValueChanged( (double)args.OldValue, (double)args.NewValue ); } } protected virtual void OnHigherValueChanged( double oldValue, double newValue ) { this.AdjustView(); RoutedEventArgs args = new RoutedEventArgs(); args.RoutedEvent = RangeSlider.HigherValueChangedEvent; this.RaiseEvent( args ); } #endregion HigherValue #region IsDeferredUpdateValues /// /// Gets/Sets if the LowerValue and HigherValue should be updated only on mouse up. /// public static readonly DependencyProperty IsDeferredUpdateValuesProperty = DependencyProperty.Register( "IsDeferredUpdateValues", typeof( bool ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( false ) ); public bool IsDeferredUpdateValues { get { return (bool)GetValue( RangeSlider.IsDeferredUpdateValuesProperty ); } set { SetValue( RangeSlider.IsDeferredUpdateValuesProperty, value ); } } #endregion IsDeferredUpdateValues #region IsSnapToTickEnabled public static readonly DependencyProperty IsSnapToTickEnabledProperty = DependencyProperty.Register( "IsSnapToTickEnabled", typeof( bool ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( false ) ); public bool IsSnapToTickEnabled { get { return (bool)GetValue( RangeSlider.IsSnapToTickEnabledProperty ); } set { SetValue( RangeSlider.IsSnapToTickEnabledProperty, value ); } } #endregion IsSnapToTickEnabled #region LowerRangeBackground /// /// Get/Set the Brush for the Range between Minimum and Lower values. (Brush) /// public static readonly DependencyProperty LowerRangeBackgroundProperty = DependencyProperty.Register( "LowerRangeBackground", typeof( Brush ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( Brushes.Transparent ) ); public Brush LowerRangeBackground { get { return (Brush)GetValue( RangeSlider.LowerRangeBackgroundProperty ); } set { SetValue( RangeSlider.LowerRangeBackgroundProperty, value ); } } #endregion LowerRangeBackground #region LowerRangeStyle /// /// Get/Set the Style for the Range between Minimum and Lower values. (Style) /// public static readonly DependencyProperty LowerRangeStyleProperty = DependencyProperty.Register( "LowerRangeStyle", typeof( Style ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( null ) ); public Style LowerRangeStyle { get { return (Style)this.GetValue( RangeSlider.LowerRangeStyleProperty ); } set { this.SetValue( RangeSlider.LowerRangeStyleProperty, value ); } } #endregion LowerRangeStyle #region LowerRangeWidth /// /// LowerRangeWidth property is a readonly property, used to calculate the percentage of the LowerRange, within the entire min/max range. /// /// private static DependencyPropertyKey LowerRangeWidthPropertyKey = DependencyProperty.RegisterAttachedReadOnly( "LowerRangeWidth", typeof( double ) , typeof( RangeSlider ), new PropertyMetadata( 0d ) ); public static readonly DependencyProperty LowerRangeWidthProperty = LowerRangeWidthPropertyKey.DependencyProperty; public double LowerRangeWidth { get { return (double)GetValue( RangeSlider.LowerRangeWidthProperty ); } private set { SetValue( RangeSlider.LowerRangeWidthPropertyKey, value ); } } #endregion LowerRangeWidth #region LowerThumbBackground /// /// Get/Set the Brush for the LowerValue thumb's background [active state]. (Brush) /// public static readonly DependencyProperty LowerThumbBackgroundProperty = DependencyProperty.Register( "LowerThumbBackground", typeof( Brush ), typeof( RangeSlider ) ); public Brush LowerThumbBackground { get { return (Brush)GetValue( RangeSlider.LowerThumbBackgroundProperty ); } set { SetValue( RangeSlider.LowerThumbBackgroundProperty, value ); } } #endregion LowerThumbBackground #region LowerValue /// /// LowerValue property represents the lower value within the selected range. /// public static readonly DependencyProperty LowerValueProperty = DependencyProperty.Register( "LowerValue", typeof( double ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( 0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, RangeSlider.OnLowerValueChanged, RangeSlider.OnCoerceLowerValueChanged ) ); public double LowerValue { get { return (double)GetValue( RangeSlider.LowerValueProperty ); } set { SetValue( RangeSlider.LowerValueProperty, value ); } } private static object OnCoerceLowerValueChanged( DependencyObject d, object basevalue ) { var rangeSlider = (RangeSlider)d; if( (rangeSlider == null) || !rangeSlider.IsLoaded ) return basevalue; var min = Math.Min( rangeSlider.Minimum, rangeSlider.Maximum ); var max = Math.Max( rangeSlider.Minimum, rangeSlider.Maximum ); var lowerValue = Math.Max( rangeSlider.Minimum, Math.Min( rangeSlider.Maximum, (double)basevalue ) ); lowerValue = Math.Min( (double)basevalue, rangeSlider.HigherValue ); return lowerValue; } private static void OnLowerValueChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args ) { RangeSlider rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnLowerValueChanged( (double)args.OldValue, (double)args.NewValue ); } } protected virtual void OnLowerValueChanged( double oldValue, double newValue ) { this.AdjustView(); RoutedEventArgs args = new RoutedEventArgs(); args.RoutedEvent = RangeSlider.LowerValueChangedEvent; this.RaiseEvent( args ); } #endregion LowerValue #region Maximum /// /// Maximum property represents the maximum value, which can be selected, in a range. /// public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( "Maximum", typeof( double ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( RangeSlider.OnMaximumChanged ) ); public double Maximum { get { return (double)GetValue( RangeSlider.MaximumProperty ); } set { SetValue( RangeSlider.MaximumProperty, value ); } } private static void OnMaximumChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args ) { RangeSlider rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnMaximumChanged( (double)args.OldValue, (double)args.NewValue ); } } protected virtual void OnMaximumChanged( double oldValue, double newValue ) { this.AdjustView(); } #endregion Maximum #region Minimum /// /// Minimum property represents the minimum value, which can be selected, in a range. /// public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( "Minimum", typeof( double ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( RangeSlider.OnMinimumChanged ) ); public double Minimum { get { return (double)GetValue( RangeSlider.MinimumProperty ); } set { SetValue( RangeSlider.MinimumProperty, value ); } } private static void OnMinimumChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args ) { RangeSlider rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnMinimumChanged( (double)args.OldValue, (double)args.NewValue ); } } protected virtual void OnMinimumChanged( double oldValue, double newValue ) { // adjust the range width this.AdjustView(); } #endregion Minimum #region Orientation /// /// Get/Set the RangeSlider's orientation. /// public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( "Orientation", typeof( Orientation ), typeof( RangeSlider ), new FrameworkPropertyMetadata( Orientation.Horizontal, RangeSlider.OnOrientationChanged ) ); public Orientation Orientation { get { return (Orientation)GetValue( RangeSlider.OrientationProperty ); } set { SetValue( RangeSlider.OrientationProperty, value ); } } private static void OnOrientationChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { RangeSlider rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnOrientationChanged( (Orientation)e.OldValue, (Orientation)e.NewValue ); } } protected virtual void OnOrientationChanged( Orientation oldValue, Orientation newValue ) { } #endregion //Orientation #region RangeBackground /// /// Get/Set the Brush for the Range between Lower and Higher values [active state]. (Brush) /// public static readonly DependencyProperty RangeBackgroundProperty = DependencyProperty.Register( "RangeBackground", typeof( Brush ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( Brushes.Transparent ) ); public Brush RangeBackground { get { return (Brush)GetValue( RangeSlider.RangeBackgroundProperty ); } set { SetValue( RangeSlider.RangeBackgroundProperty, value ); } } #endregion RangeBackground #region RangeStyle /// /// Get/Set the Style for the Range between Lower and Higher values. (Style) /// public static readonly DependencyProperty RangeStyleProperty = DependencyProperty.Register( "RangeStyle", typeof( Style ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( null ) ); public Style RangeStyle { get { return (Style)this.GetValue( RangeSlider.RangeStyleProperty ); } set { this.SetValue( RangeSlider.RangeStyleProperty, value ); } } #endregion RangeStyle #region RangeWidth /// /// RangeWidth property is a readonly property, used to calculate the percentage of the range within the entire min/max range. /// private static readonly DependencyPropertyKey RangeWidthPropertyKey = DependencyProperty.RegisterAttachedReadOnly( "RangeWidth", typeof( double ) , typeof( RangeSlider ), new PropertyMetadata( 0d ) ); public static readonly DependencyProperty RangeWidthProperty = RangeWidthPropertyKey.DependencyProperty; public double RangeWidth { get { return (double)GetValue( RangeSlider.RangeWidthProperty ); } private set { SetValue( RangeSlider.RangeWidthPropertyKey, value ); } } #endregion RangeWidth #region Step /// /// Step property is used to identify the RangeSlider's size of individual move, when clicking on the LowerRange, HigherRange, not while scrolling the thumbs. /// private static readonly DependencyProperty StepProperty = DependencyProperty.Register( "Step", typeof( double ), typeof( RangeSlider ) , new PropertyMetadata( 1.0, null, RangeSlider.CoerceStep ) ); public double Step { get { return (double)GetValue( RangeSlider.StepProperty ); } set { SetValue( RangeSlider.StepProperty, value ); } } private static object CoerceStep( DependencyObject sender, object value ) { RangeSlider rangeSlider = sender as RangeSlider; double newValue = (double)value; return Math.Max( 0.01, newValue ); } #endregion #region TickFrequency /// /// Gets or sets the interval between tick marks. /// public static readonly DependencyProperty TickFrequencyProperty = DependencyProperty.Register( "TickFrequency", typeof( double ), typeof( RangeSlider ) , new FrameworkPropertyMetadata( 1d, RangeSlider.OnTickFrequencyChanged ) ); public double TickFrequency { get { return (double)GetValue( RangeSlider.TickFrequencyProperty ); } set { SetValue( RangeSlider.TickFrequencyProperty, value ); } } private static void OnTickFrequencyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs args ) { var rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnTickFrequencyChanged( (double)args.OldValue, (double)args.NewValue ); } } protected virtual void OnTickFrequencyChanged( double oldValue, double newValue ) { } #endregion TickFrequency #region TickPlacement public static readonly DependencyProperty TickPlacementProperty = DependencyProperty.Register( "TickPlacement", typeof( TickPlacement ), typeof( RangeSlider ), new FrameworkPropertyMetadata( TickPlacement.None, RangeSlider.OnTickPlacementChanged ) ); public TickPlacement TickPlacement { get { return (TickPlacement)GetValue( RangeSlider.TickPlacementProperty ); } set { SetValue( RangeSlider.TickPlacementProperty, value ); } } private static void OnTickPlacementChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { var rangeSlider = sender as RangeSlider; if( rangeSlider != null ) { rangeSlider.OnTickPlacementChanged( (TickPlacement)e.OldValue, (TickPlacement)e.NewValue ); } } protected virtual void OnTickPlacementChanged( TickPlacement oldValue, TickPlacement newValue ) { } #endregion //TickPlacement #endregion Properties #region Override public override void OnApplyTemplate() { base.OnApplyTemplate(); if( _lowerRange != null ) { _lowerRange.Click -= new RoutedEventHandler( this.LowerRange_Click ); } _lowerRange = this.Template.FindName( PART_LowerRange, this ) as RepeatButton; if( _lowerRange != null ) { _lowerRange.Click += new RoutedEventHandler( this.LowerRange_Click ); } if( _higherRange != null ) { _higherRange.Click -= new RoutedEventHandler( this.HigherRange_Click ); } _higherRange = this.Template.FindName( PART_HigherRange, this ) as RepeatButton; if( _higherRange != null ) { _higherRange.Click += new RoutedEventHandler( this.HigherRange_Click ); } if( _lowerSlider != null ) { _lowerSlider.Loaded -= this.Slider_Loaded; _lowerSlider.ValueChanged -= LowerSlider_ValueChanged; if( _lowerTrack != null ) { _lowerTrack.Thumb.DragCompleted -= this.LowerSlider_DragCompleted; } } _lowerSlider = this.Template.FindName( PART_LowerSlider, this ) as Slider; if( _lowerSlider != null ) { _lowerSlider.Loaded += this.Slider_Loaded; _lowerSlider.ValueChanged += LowerSlider_ValueChanged; _lowerSlider.ApplyTemplate(); _lowerTrack = _lowerSlider.Template.FindName( PART_Track, _lowerSlider ) as Track; if( _lowerTrack != null ) { _lowerTrack.Thumb.DragCompleted += this.LowerSlider_DragCompleted; } } if( _higherSlider != null ) { _higherSlider.Loaded -= this.Slider_Loaded; _higherSlider.ValueChanged -= HigherSlider_ValueChanged; if( _higherTrack != null ) { _higherTrack.Thumb.DragCompleted -= this.HigherSlider_DragCompleted; } } _higherSlider = this.Template.FindName( PART_HigherSlider, this ) as Slider; if( _higherSlider != null ) { _higherSlider.Loaded += this.Slider_Loaded; _higherSlider.ValueChanged += HigherSlider_ValueChanged; _higherSlider.ApplyTemplate(); _higherTrack = _higherSlider.Template.FindName( PART_Track, _higherSlider ) as Track; if( _higherTrack != null ) { _higherTrack.Thumb.DragCompleted += this.HigherSlider_DragCompleted; } } } public override string ToString() { return this.LowerValue.ToString() + "-" + this.HigherValue.ToString(); } #endregion Override #region Methods internal static double GetThumbWidth( Slider slider ) { if( slider != null ) { var track = (Track)slider.Template.FindName( "PART_Track", slider ); if( track != null ) { var thumb = track.Thumb; return thumb.ActualWidth; } } return 0d; } internal static double GetThumbHeight( Slider slider ) { if( slider != null ) { var track = (Track)slider.Template.FindName( "PART_Track", slider ); if( track != null ) { var thumb = track.Thumb; return thumb.ActualHeight; } } return 0d; } private void AdjustView() { //Coerce values to make them consistent. var cv = this.GetCoercedValues(); double actualWidth = 0; double lowerSliderThumbWidth = 0d; double higherSliderThumbWidth = 0d; if( this.Orientation == Orientation.Horizontal ) { actualWidth = this.ActualWidth; lowerSliderThumbWidth = RangeSlider.GetThumbWidth( _lowerSlider ); higherSliderThumbWidth = RangeSlider.GetThumbWidth( _higherSlider ); } else if( this.Orientation == Orientation.Vertical ) { actualWidth = this.ActualHeight; lowerSliderThumbWidth = RangeSlider.GetThumbHeight( _lowerSlider ); higherSliderThumbWidth = RangeSlider.GetThumbHeight( _higherSlider ); } actualWidth -= (lowerSliderThumbWidth + higherSliderThumbWidth); this.SetLowerSliderValues( cv.LowerValue, cv.Minimum, cv.Maximum ); this.SetHigherSliderValues( cv.HigherValue, cv.Minimum, cv.Maximum ); double entireRange = cv.Maximum - cv.Minimum; if( entireRange > 0 ) { this.HigherRangeWidth = (actualWidth * (cv.Maximum - cv.HigherValue)) / entireRange; this.RangeWidth = (actualWidth * (cv.HigherValue - cv.LowerValue)) / entireRange; this.LowerRangeWidth = (actualWidth * (cv.LowerValue - cv.Minimum)) / entireRange; } else { this.HigherRangeWidth = 0d; this.RangeWidth = 0d; this.LowerRangeWidth = actualWidth; } } private void SetSlidersMargins() { if( (_lowerSlider != null) && (_higherSlider != null) ) { if( this.Orientation == Orientation.Horizontal ) { double lowerSliderThumbWidth = RangeSlider.GetThumbWidth( _lowerSlider ); double higherSliderThumbWidth = RangeSlider.GetThumbWidth( _higherSlider ); _higherSlider.Margin = new Thickness( lowerSliderThumbWidth, 0d, 0d, 0d ); _lowerSlider.Margin = new Thickness( 0d, 0d, higherSliderThumbWidth, 0d ); } else { double lowerSliderThumbHeight = RangeSlider.GetThumbHeight( _lowerSlider ); double higherSliderThumbHeight = RangeSlider.GetThumbHeight( _higherSlider ); _higherSlider.Margin = new Thickness( 0d, 0d, 0d, lowerSliderThumbHeight ); _lowerSlider.Margin = new Thickness( 0d, higherSliderThumbHeight, 0d, 0d ); } } } private CoercedValues GetCoercedValues() { CoercedValues cv = new CoercedValues(); cv.Minimum = Math.Min( this.Minimum, this.Maximum ); cv.Maximum = Math.Max( cv.Minimum, this.Maximum ); cv.LowerValue = Math.Max( cv.Minimum, Math.Min( cv.Maximum, this.LowerValue ) ); cv.HigherValue = Math.Max( cv.Minimum, Math.Min( cv.Maximum, this.HigherValue ) ); cv.HigherValue = Math.Max( cv.LowerValue, cv.HigherValue ); return cv; } private void SetLowerSliderValues( double value, double? minimum, double? maximum ) { this.SetSliderValues( _lowerSlider, this.LowerSlider_ValueChanged, value, minimum, maximum ); } private void SetHigherSliderValues( double value, double? minimum, double? maximum ) { this.SetSliderValues( _higherSlider, this.HigherSlider_ValueChanged, value, minimum, maximum ); } private void SetSliderValues( Slider slider, RoutedPropertyChangedEventHandler handler, double value, double? minimum, double? maximum ) { if( slider != null ) { slider.ValueChanged -= handler; slider.Value = value; if( minimum != null ) { slider.Minimum = minimum.Value; } if( maximum != null ) { slider.Maximum = maximum.Value; } slider.ValueChanged += handler; } } private void UpdateHigherValue( double value ) { CoercedValues cv = this.GetCoercedValues(); double newValue = Math.Max( cv.Minimum, Math.Min( cv.Maximum, value ) ); newValue = Math.Max( newValue, cv.LowerValue ); this.SetHigherSliderValues( newValue, null, null ); this.HigherValue = newValue; } private void UpdateLowerValue( double value ) { CoercedValues cv = this.GetCoercedValues(); double newValue = Math.Max( cv.Minimum, Math.Min( cv.Maximum, value ) ); newValue = Math.Min( newValue, cv.HigherValue ); this.SetLowerSliderValues( newValue, null, null ); this.LowerValue = newValue; } #endregion #region Events public static readonly RoutedEvent LowerValueChangedEvent = EventManager.RegisterRoutedEvent( "LowerValueChanged", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( RangeSlider ) ); public event RoutedEventHandler LowerValueChanged { add { AddHandler( RangeSlider.LowerValueChangedEvent, value ); } remove { RemoveHandler( RangeSlider.LowerValueChangedEvent, value ); } } public static readonly RoutedEvent HigherValueChangedEvent = EventManager.RegisterRoutedEvent( "HigherValueChanged", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( RangeSlider ) ); public event RoutedEventHandler HigherValueChanged { add { AddHandler( RangeSlider.HigherValueChangedEvent, value ); } remove { RemoveHandler( RangeSlider.HigherValueChangedEvent, value ); } } #endregion //Events #region Events Handlers private void LowerRange_Click( object sender, RoutedEventArgs e ) { CoercedValues cv = this.GetCoercedValues(); //When Maximum is not greater than Minimum, the //slider display is in an inconsistant state. Don't //consider any operation from the user if( cv.Minimum < cv.Maximum ) { double newValue = cv.LowerValue - this.Step; this.LowerValue = Math.Min( cv.Maximum, Math.Max( cv.Minimum, newValue ) ); } } private void HigherRange_Click( object sender, RoutedEventArgs e ) { CoercedValues cv = this.GetCoercedValues(); //When Maximum is not greater than Minimum, the //slider display is in an inconsistant state. Don't //consider any operation from the user if( cv.Minimum < cv.Maximum ) { double newValue = cv.HigherValue + this.Step; this.HigherValue = Math.Min( cv.Maximum, Math.Max( cv.Minimum, newValue ) ); } } private void RangeSlider_SizeChanged( object sender, SizeChangedEventArgs e ) { this.AdjustView(); } private void Slider_Loaded( object sender, RoutedEventArgs e ) { this.SetSlidersMargins(); this.AdjustView(); } private void LowerSlider_ValueChanged( object sender, RoutedPropertyChangedEventArgs e ) { if( (_lowerSlider != null) && _lowerSlider.IsLoaded ) { if( !this.IsDeferredUpdateValues ) { this.UpdateLowerValue( e.NewValue ); } else { _deferredUpdateValue = e.NewValue; } } } private void HigherSlider_ValueChanged( object sender, RoutedPropertyChangedEventArgs e ) { if( ( _higherSlider != null) && _higherSlider.IsLoaded ) { if( !this.IsDeferredUpdateValues ) { this.UpdateHigherValue( e.NewValue ); } else { _deferredUpdateValue = e.NewValue; } } } private void HigherSlider_DragCompleted( object sender, DragCompletedEventArgs e ) { if( this.IsDeferredUpdateValues ) { this.UpdateHigherValue( _deferredUpdateValue ); } } private void LowerSlider_DragCompleted( object sender, DragCompletedEventArgs e ) { if( this.IsDeferredUpdateValues ) { this.UpdateLowerValue( _deferredUpdateValue ); } } #endregion Events Handlers private struct CoercedValues { public double Minimum; public double Maximum; public double LowerValue; public double HigherValue; } } }