You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1043 lines
32 KiB
1043 lines
32 KiB
/*************************************************************************************
|
|
|
|
Toolkit for WPF
|
|
|
|
Copyright (C) 2007-2025 Xceed Software Inc.
|
|
|
|
This program is provided to you under the terms of the XCEED SOFTWARE, INC.
|
|
COMMUNITY LICENSE AGREEMENT (for non-commercial use) as published at
|
|
https://github.com/xceedsoftware/wpftoolkit/blob/master/license.md
|
|
|
|
For more features, controls, and fast professional support,
|
|
pick up the Plus Edition at https://xceed.com/xceed-toolkit-plus-for-wpf/
|
|
|
|
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()
|
|
{
|
|
|
|
Core.Message.ShowMessage();
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
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
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
|
|
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
|
|
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
|
|
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( bool isHigherValueChanged = false )
|
|
{
|
|
//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 );
|
|
|
|
if( !this.IsDeferredUpdateValues || ( _deferredUpdateValue == null ) )
|
|
{
|
|
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 )
|
|
{
|
|
var higherValue = this.IsDeferredUpdateValues && isHigherValueChanged && ( _deferredUpdateValue != null ) ? _deferredUpdateValue.Value : cv.HigherValue;
|
|
var lowerValue = this.IsDeferredUpdateValues && !isHigherValueChanged && ( _deferredUpdateValue != null ) ? _deferredUpdateValue.Value : cv.LowerValue;
|
|
|
|
this.HigherRangeWidth = ( actualWidth * ( cv.Maximum - higherValue ) ) / entireRange;
|
|
|
|
this.RangeWidth = ( actualWidth * ( higherValue - lowerValue ) ) / entireRange;
|
|
|
|
this.LowerRangeWidth = ( actualWidth * ( 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<double> 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.HasValue ? value.Value : 0d ) );
|
|
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.HasValue ? value.Value : 0d ) );
|
|
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<double> e )
|
|
{
|
|
if( ( _lowerSlider != null ) && _lowerSlider.IsLoaded )
|
|
{
|
|
if( !this.IsDeferredUpdateValues )
|
|
{
|
|
this.UpdateLowerValue( e.NewValue );
|
|
}
|
|
else
|
|
{
|
|
_deferredUpdateValue = e.NewValue;
|
|
this.AdjustView( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HigherSlider_ValueChanged( object sender, RoutedPropertyChangedEventArgs<double> e )
|
|
{
|
|
if( ( _higherSlider != null ) && _higherSlider.IsLoaded )
|
|
{
|
|
if( !this.IsDeferredUpdateValues )
|
|
{
|
|
this.UpdateHigherValue( e.NewValue );
|
|
}
|
|
else
|
|
{
|
|
_deferredUpdateValue = e.NewValue;
|
|
this.AdjustView( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HigherSlider_DragCompleted( object sender, DragCompletedEventArgs e )
|
|
{
|
|
if( this.IsDeferredUpdateValues )
|
|
{
|
|
this.UpdateHigherValue( _deferredUpdateValue );
|
|
_deferredUpdateValue = null;
|
|
this.AdjustView();
|
|
}
|
|
}
|
|
|
|
private void LowerSlider_DragCompleted( object sender, DragCompletedEventArgs e )
|
|
{
|
|
if( this.IsDeferredUpdateValues )
|
|
{
|
|
this.UpdateLowerValue( _deferredUpdateValue );
|
|
_deferredUpdateValue = null;
|
|
this.AdjustView();
|
|
}
|
|
}
|
|
|
|
#endregion Events Handlers
|
|
|
|
private struct CoercedValues
|
|
{
|
|
public double Minimum;
|
|
public double Maximum;
|
|
public double LowerValue;
|
|
public double HigherValue;
|
|
}
|
|
}
|
|
}
|
|
|