PageRenderTime 46ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/MahApps.Metro/Controls/RangeSlider.cs

https://github.com/smiron/MahApps.Metro
C# | 2439 lines | 2017 code | 233 blank | 189 comment | 391 complexity | 69e258185c7f33d9ebf3c7c05a756a8e MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. using System;
  2. using System.ComponentModel;
  3. using System.Globalization;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Controls.Primitives;
  7. using System.Windows.Input;
  8. using System.Windows.Threading;
  9. namespace MahApps.Metro.Controls
  10. {
  11. public delegate void RangeSelectionChangedEventHandler(object sender, RangeSelectionChangedEventArgs e);
  12. public delegate void RangeParameterChangedEventHandler(object sender, RangeParameterChangedEventArgs e);
  13. /// <summary>
  14. /// A slider control with the ability to select a range between two values.
  15. /// </summary>
  16. [DefaultEvent("RangeSelectionChanged"),
  17. TemplatePart(Name = "PART_Container", Type = typeof(StackPanel)),
  18. TemplatePart(Name = "PART_RangeSliderContainer", Type = typeof(StackPanel)),
  19. TemplatePart(Name = "PART_LeftEdge", Type = typeof(RepeatButton)),
  20. TemplatePart(Name = "PART_RightEdge", Type = typeof(RepeatButton)),
  21. TemplatePart(Name = "PART_LeftThumb", Type = typeof(Thumb)),
  22. TemplatePart(Name = "PART_MiddleThumb", Type = typeof(Thumb)),
  23. TemplatePart(Name = "PART_PART_TopTick", Type = typeof(TickBar)),
  24. TemplatePart(Name = "PART_PART_BottomTick", Type = typeof(TickBar)),
  25. TemplatePart(Name = "PART_RightThumb", Type = typeof(Thumb))]
  26. public class RangeSlider : RangeBase
  27. {
  28. #region Routed UI commands
  29. public static RoutedUICommand MoveBack = new RoutedUICommand("MoveBack", "MoveBack", typeof (RangeSlider),
  30. new InputGestureCollection(new InputGesture[] {new KeyGesture(Key.B, ModifierKeys.Control)}));
  31. public static RoutedUICommand MoveForward = new RoutedUICommand("MoveForward", "MoveForward",
  32. typeof (RangeSlider),
  33. new InputGestureCollection(new InputGesture[] {new KeyGesture(Key.F, ModifierKeys.Control)}));
  34. public static RoutedUICommand MoveAllForward = new RoutedUICommand("MoveAllForward", "MoveAllForward",
  35. typeof (RangeSlider),
  36. new InputGestureCollection(new InputGesture[] {new KeyGesture(Key.F, ModifierKeys.Alt)}));
  37. public static RoutedUICommand MoveAllBack = new RoutedUICommand("MoveAllBack", "MoveAllBack",
  38. typeof (RangeSlider),
  39. new InputGestureCollection(new InputGesture[] {new KeyGesture(Key.B, ModifierKeys.Alt)}));
  40. #endregion
  41. #region Routed events
  42. public static readonly RoutedEvent RangeSelectionChangedEvent =
  43. EventManager.RegisterRoutedEvent("RangeSelectionChanged", RoutingStrategy.Bubble,
  44. typeof (RangeSelectionChangedEventHandler), typeof (RangeSlider));
  45. public static readonly RoutedEvent LowerValueChangedEvent =
  46. EventManager.RegisterRoutedEvent("LowerValueChanged", RoutingStrategy.Bubble,
  47. typeof (RangeParameterChangedEventHandler), typeof (RangeSlider));
  48. public static readonly RoutedEvent UpperValueChangedEvent =
  49. EventManager.RegisterRoutedEvent("UpperValueChanged", RoutingStrategy.Bubble,
  50. typeof (RangeParameterChangedEventHandler), typeof (RangeSlider));
  51. public static readonly RoutedEvent LowerThumbDragStartedEvent =
  52. EventManager.RegisterRoutedEvent("LowerThumbDragStarted", RoutingStrategy.Bubble,
  53. typeof (DragStartedEventHandler), typeof (RangeSlider));
  54. public static readonly RoutedEvent LowerThumbDragCompletedEvent =
  55. EventManager.RegisterRoutedEvent("LowerThumbDragCompleted", RoutingStrategy.Bubble,
  56. typeof (DragCompletedEventHandler), typeof (RangeSlider));
  57. public static readonly RoutedEvent UpperThumbDragStartedEvent =
  58. EventManager.RegisterRoutedEvent("UpperThumbDragStarted", RoutingStrategy.Bubble,
  59. typeof (DragStartedEventHandler), typeof (RangeSlider));
  60. public static readonly RoutedEvent UpperThumbDragCompletedEvent =
  61. EventManager.RegisterRoutedEvent("UpperThumbDragCompleted", RoutingStrategy.Bubble,
  62. typeof (DragCompletedEventHandler), typeof (RangeSlider));
  63. public static readonly RoutedEvent CentralThumbDragStartedEvent =
  64. EventManager.RegisterRoutedEvent("CentralThumbDragStarted", RoutingStrategy.Bubble,
  65. typeof(DragStartedEventHandler), typeof(RangeSlider));
  66. public static readonly RoutedEvent CentralThumbDragCompletedEvent =
  67. EventManager.RegisterRoutedEvent("CentralThumbDragCompleted", RoutingStrategy.Bubble,
  68. typeof(DragCompletedEventHandler), typeof(RangeSlider));
  69. public static readonly RoutedEvent LowerThumbDragDeltaEvent =
  70. EventManager.RegisterRoutedEvent("LowerThumbDragDelta", RoutingStrategy.Bubble,
  71. typeof(DragDeltaEventHandler), typeof(RangeSlider));
  72. public static readonly RoutedEvent UpperThumbDragDeltaEvent =
  73. EventManager.RegisterRoutedEvent("UpperThumbDragDelta", RoutingStrategy.Bubble,
  74. typeof(DragDeltaEventHandler), typeof(RangeSlider));
  75. public static readonly RoutedEvent CentralThumbDragDeltaEvent =
  76. EventManager.RegisterRoutedEvent("CentralThumbDragDelta", RoutingStrategy.Bubble,
  77. typeof(DragDeltaEventHandler), typeof(RangeSlider));
  78. #endregion
  79. #region Event handlers
  80. public event RangeSelectionChangedEventHandler RangeSelectionChanged
  81. {
  82. add { AddHandler(RangeSelectionChangedEvent, value); }
  83. remove { RemoveHandler(RangeSelectionChangedEvent, value); }
  84. }
  85. public event RangeParameterChangedEventHandler LowerValueChanged
  86. {
  87. add { AddHandler(LowerValueChangedEvent, value); }
  88. remove { RemoveHandler(LowerValueChangedEvent, value); }
  89. }
  90. public event RangeParameterChangedEventHandler UpperValueChanged
  91. {
  92. add { AddHandler(UpperValueChangedEvent, value); }
  93. remove { RemoveHandler(UpperValueChangedEvent, value); }
  94. }
  95. public event DragStartedEventHandler LowerThumbDragStarted
  96. {
  97. add { AddHandler(LowerThumbDragStartedEvent, value); }
  98. remove { RemoveHandler(LowerThumbDragStartedEvent, value); }
  99. }
  100. public event DragCompletedEventHandler LowerThumbDragCompleted
  101. {
  102. add { AddHandler(LowerThumbDragCompletedEvent, value); }
  103. remove { RemoveHandler(LowerThumbDragCompletedEvent, value); }
  104. }
  105. public event DragStartedEventHandler UpperThumbDragStarted
  106. {
  107. add { AddHandler(UpperThumbDragStartedEvent, value); }
  108. remove { RemoveHandler(UpperThumbDragStartedEvent, value); }
  109. }
  110. public event DragCompletedEventHandler UpperThumbDragCompleted
  111. {
  112. add { AddHandler(UpperThumbDragCompletedEvent, value); }
  113. remove { RemoveHandler(UpperThumbDragCompletedEvent, value); }
  114. }
  115. public event DragStartedEventHandler CentralThumbDragStarted
  116. {
  117. add { AddHandler(CentralThumbDragStartedEvent, value); }
  118. remove { RemoveHandler(CentralThumbDragStartedEvent, value); }
  119. }
  120. public event DragCompletedEventHandler CentralThumbDragCompleted
  121. {
  122. add { AddHandler(CentralThumbDragCompletedEvent, value); }
  123. remove { RemoveHandler(CentralThumbDragCompletedEvent, value); }
  124. }
  125. public event DragDeltaEventHandler LowerThumbDragDelta
  126. {
  127. add { AddHandler(LowerThumbDragDeltaEvent, value); }
  128. remove { RemoveHandler(LowerThumbDragDeltaEvent, value); }
  129. }
  130. public event DragDeltaEventHandler UpperThumbDragDelta
  131. {
  132. add { AddHandler(UpperThumbDragDeltaEvent, value); }
  133. remove { RemoveHandler(UpperThumbDragDeltaEvent, value); }
  134. }
  135. public event DragDeltaEventHandler CentralThumbDragDelta
  136. {
  137. add { AddHandler(CentralThumbDragDeltaEvent, value); }
  138. remove { RemoveHandler(CentralThumbDragDeltaEvent, value); }
  139. }
  140. #endregion
  141. #region Dependency properties
  142. public static readonly DependencyProperty UpperValueProperty =
  143. DependencyProperty.Register("UpperValue", typeof (Double), typeof (RangeSlider),
  144. new FrameworkPropertyMetadata((Double) 0,
  145. FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsRender, RangesChanged, CoerceUpperValue));
  146. public static readonly DependencyProperty LowerValueProperty =
  147. DependencyProperty.Register("LowerValue", typeof(Double), typeof(RangeSlider),
  148. new FrameworkPropertyMetadata((Double)0,
  149. FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsRender, RangesChanged, CoerceLowerValue));
  150. public static readonly DependencyProperty MinRangeProperty =
  151. DependencyProperty.Register("MinRange", typeof (Double), typeof (RangeSlider),
  152. new FrameworkPropertyMetadata((Double)0, MinRangeChanged, CoerceMinRange), IsValidMinRange);
  153. public static readonly DependencyProperty MinRangeWidthProperty =
  154. DependencyProperty.Register("MinRangeWidth", typeof(Double), typeof(RangeSlider),
  155. new FrameworkPropertyMetadata(30.0, MinRangeWidthChanged, CoerceMinRangeWidth), IsValidMinRange);
  156. public static readonly DependencyProperty MoveWholeRangeProperty =
  157. DependencyProperty.Register("MoveWholeRange", typeof(Boolean), typeof(RangeSlider),
  158. new PropertyMetadata(false));
  159. public static readonly DependencyProperty ExtendedModeProperty =
  160. DependencyProperty.Register("ExtendedMode", typeof(Boolean), typeof(RangeSlider),
  161. new PropertyMetadata(false));
  162. public static readonly DependencyProperty IsSnapToTickEnabledProperty =
  163. DependencyProperty.Register("IsSnapToTickEnabled", typeof(Boolean), typeof(RangeSlider),
  164. new PropertyMetadata(false));
  165. public static readonly DependencyProperty OrientationProperty =
  166. DependencyProperty.Register("Orientation", typeof(Orientation), typeof(RangeSlider),
  167. new FrameworkPropertyMetadata(Orientation.Horizontal));
  168. public static readonly DependencyProperty TickFrequencyProperty =
  169. DependencyProperty.Register("TickFrequency", typeof(Double), typeof(RangeSlider),
  170. new FrameworkPropertyMetadata(1.0), IsValidTickFrequency);
  171. public static readonly DependencyProperty IsMoveToPointEnabledProperty =
  172. DependencyProperty.Register("IsMoveToPointEnabled", typeof(Boolean), typeof(RangeSlider),
  173. new PropertyMetadata(false));
  174. public static readonly DependencyProperty TickPlacementProperty =
  175. DependencyProperty.Register("TickPlacement", typeof(TickPlacement), typeof(RangeSlider),
  176. new FrameworkPropertyMetadata(TickPlacement.None));
  177. public static readonly DependencyProperty AutoToolTipPlacementProperty =
  178. DependencyProperty.Register("AutoToolTipPlacement", typeof(AutoToolTipPlacement), typeof(RangeSlider),
  179. new FrameworkPropertyMetadata(AutoToolTipPlacement.None));
  180. public static readonly DependencyProperty AutoToolTipPrecisionProperty =
  181. DependencyProperty.Register("AutoToolTipPrecision", typeof (Int32), typeof (RangeSlider),
  182. new FrameworkPropertyMetadata(0), IsValidPrecision);
  183. public static readonly DependencyProperty IntervalProperty =
  184. DependencyProperty.Register("Interval", typeof(Int32), typeof(RangeSlider),
  185. new FrameworkPropertyMetadata(100, IntervalChangedCallback), IsValidPrecision);
  186. /// <summary>
  187. /// Get/sets value how fast thumbs will move when user press on left/right/central with left mouse button (IsMoveToPoint must be set to FALSE)
  188. /// </summary>
  189. [Bindable(true), Category("Behavior")]
  190. public Int32 Interval
  191. {
  192. get { return (Int32)GetValue(IntervalProperty); }
  193. set { SetValue(IntervalProperty, value); }
  194. }
  195. /// <summary>
  196. /// Get/sets precision of the value, which displaying inside AutotToolTip
  197. /// </summary>
  198. [Bindable(true), Category("Appearance")]
  199. public Int32 AutoToolTipPrecision
  200. {
  201. get { return (Int32)GetValue(AutoToolTipPrecisionProperty); }
  202. set { SetValue(AutoToolTipPrecisionProperty, value); }
  203. }
  204. /// <summary>
  205. /// Get/sets tooltip, which will show while dragging thumbs and display currect value
  206. /// </summary>
  207. [Bindable(true), Category("Behavior")]
  208. public AutoToolTipPlacement AutoToolTipPlacement
  209. {
  210. get { return (AutoToolTipPlacement)GetValue(AutoToolTipPlacementProperty); }
  211. set { SetValue(AutoToolTipPlacementProperty, value); }
  212. }
  213. /// <summary>
  214. /// Get/sets tick placement position
  215. /// </summary>
  216. [Bindable(true), Category("Common")]
  217. public TickPlacement TickPlacement
  218. {
  219. get { return (TickPlacement)GetValue(TickPlacementProperty); }
  220. set { SetValue(TickPlacementProperty, value); }
  221. }
  222. /// <summary>
  223. /// Get/sets IsMoveToPoint feature which will enable/disable moving to exact point inside control when user clicked on it
  224. /// </summary>
  225. [Bindable(true), Category("Common")]
  226. public Boolean IsMoveToPointEnabled
  227. {
  228. get { return (Boolean)GetValue(IsMoveToPointEnabledProperty); }
  229. set { SetValue(IsMoveToPointEnabledProperty, value); }
  230. }
  231. /// <summary>
  232. /// Get/sets tickFrequency
  233. /// </summary>
  234. [Bindable(true), Category("Common")]
  235. public Double TickFrequency
  236. {
  237. get { return (Double)GetValue(TickFrequencyProperty); }
  238. set { SetValue(TickFrequencyProperty, value); }
  239. }
  240. /// <summary>
  241. /// Get/sets orientation of range slider
  242. /// </summary>
  243. [Bindable(true), Category("Common")]
  244. public Orientation Orientation
  245. {
  246. get { return (Orientation)GetValue(OrientationProperty); }
  247. set { SetValue(OrientationProperty, value); }
  248. }
  249. /// <summary>
  250. /// Get/sets whether possibility to make manipulations inside range with left/right mouse buttons + cotrol button
  251. /// </summary>
  252. [Bindable(true), Category("Appearance")]
  253. public Boolean IsSnapToTickEnabled
  254. {
  255. get { return (Boolean)GetValue(IsSnapToTickEnabledProperty); }
  256. set { SetValue(IsSnapToTickEnabledProperty, value); }
  257. }
  258. /// <summary>
  259. /// Get/sets whether possibility to make manipulations inside range with left/right mouse buttons + cotrol button
  260. /// </summary>
  261. [Bindable(true), Category("Behavior")]
  262. public Boolean ExtendedMode
  263. {
  264. get { return (Boolean)GetValue(ExtendedModeProperty); }
  265. set { SetValue(ExtendedModeProperty, value); }
  266. }
  267. /// <summary>
  268. /// Get/sets whether whole range will be moved when press on right/left/central part of control
  269. /// </summary>
  270. [Bindable(true), Category("Behavior")]
  271. public Boolean MoveWholeRange
  272. {
  273. get { return (Boolean)GetValue(MoveWholeRangeProperty); }
  274. set { SetValue(MoveWholeRangeProperty, value); }
  275. }
  276. /// <summary>
  277. /// Get/sets the minimal distance between two thumbs.
  278. /// </summary>
  279. [Bindable(true), Category("Common")]
  280. public Double MinRangeWidth
  281. {
  282. get { return (Double)GetValue(MinRangeWidthProperty); }
  283. set { SetValue(MinRangeWidthProperty, value); }
  284. }
  285. /// <summary>
  286. /// Get/sets the beginning of the range selection.
  287. /// </summary>
  288. [Bindable(true), Category("Common")]
  289. public Double LowerValue
  290. {
  291. get { return (Double) GetValue(LowerValueProperty); }
  292. set { SetValue(LowerValueProperty, value); }
  293. }
  294. /// <summary>
  295. /// Get/sets the end of the range selection.
  296. /// </summary>
  297. [Bindable(true), Category("Common")]
  298. public Double UpperValue
  299. {
  300. get { return (Double) GetValue(UpperValueProperty); }
  301. set { SetValue(UpperValueProperty, value); }
  302. }
  303. /// <summary>
  304. /// Get/sets the minimum range that can be selected.
  305. /// </summary>
  306. [Bindable(true), Category("Common")]
  307. public Double MinRange
  308. {
  309. get { return (Double) GetValue(MinRangeProperty); }
  310. set { SetValue(MinRangeProperty, value); }
  311. }
  312. #endregion
  313. #region Variables
  314. private const double Epsilon = 0.00000153;
  315. private Boolean _internalUpdate;
  316. private Thumb _centerThumb;
  317. private Thumb _leftThumb;
  318. private Thumb _rightThumb;
  319. private RepeatButton _leftButton;
  320. private RepeatButton _rightButton;
  321. private StackPanel _visualElementsContainer;
  322. private StackPanel _container;
  323. private Double _movableWidth;
  324. private readonly DispatcherTimer _timer;
  325. private uint _tickCount;
  326. private Double _currentpoint;
  327. private Boolean _isInsideRange;
  328. private Boolean _centerThumbBlocked;
  329. private Direction _direction;
  330. private ButtonType _bType;
  331. private Point _position;
  332. private Point _basePoint;
  333. private Double _currenValue;
  334. private Double _density;
  335. private ToolTip _autoToolTip;
  336. Double _oldLower;
  337. Double _oldUpper;
  338. private Boolean _isMoved;
  339. private Boolean _roundToPrecision;
  340. private Int32 _precision;
  341. #endregion
  342. public double MovableRange
  343. {
  344. get
  345. {
  346. return Maximum - Minimum - MinRange;
  347. }
  348. }
  349. public RangeSlider()
  350. {
  351. CommandBindings.Add(new CommandBinding(MoveBack, MoveBackHandler));
  352. CommandBindings.Add(new CommandBinding(MoveForward, MoveForwardHandler));
  353. CommandBindings.Add(new CommandBinding(MoveAllForward, MoveAllForwardHandler));
  354. CommandBindings.Add(new CommandBinding(MoveAllBack, MoveAllBackHandler));
  355. DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(RangeSlider)).AddValueChanged(this, delegate { ReCalculateSize(); });
  356. DependencyPropertyDescriptor.FromProperty(ActualHeightProperty, typeof(RangeSlider)).AddValueChanged(this, delegate { ReCalculateSize(); });
  357. _timer = new DispatcherTimer();
  358. _timer.Tick += MoveToNextValue;
  359. _timer.Interval = TimeSpan.FromMilliseconds(Interval);
  360. }
  361. static RangeSlider()
  362. {
  363. DefaultStyleKeyProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(typeof(RangeSlider)));
  364. MinimumProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsMeasure, MinPropertyChangedCallback, CoerceMinimum));
  365. MaximumProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsMeasure, MaxPropertyChangedCallback, CoerceMaximum));
  366. }
  367. /// <summary>
  368. /// Responds to a change in the value of the <see cref="P:System.Windows.Controls.Primitives.RangeBase.Minimum"/> property.
  369. /// </summary>
  370. /// <param name="oldMinimum">The old value of the <see cref="P:System.Windows.Controls.Primitives.RangeBase.Minimum"/> property.</param><param name="newMinimum">The new value of the <see cref="P:System.Windows.Controls.Primitives.RangeBase.Minimum"/> property.</param>
  371. protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
  372. {
  373. ReCalculateSize();
  374. }
  375. /// <summary>
  376. /// Responds to a change in the value of the <see cref="P:System.Windows.Controls.Primitives.RangeBase.Maximum"/> property.
  377. /// </summary>
  378. /// <param name="oldMaximum">The old value of the <see cref="P:System.Windows.Controls.Primitives.RangeBase.Maximum"/> property.</param><param name="newMaximum">The new value of the <see cref="P:System.Windows.Controls.Primitives.RangeBase.Maximum"/> property.</param>
  379. protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
  380. {
  381. ReCalculateSize();
  382. }
  383. void MoveAllBackHandler(object sender, ExecutedRoutedEventArgs e)
  384. {
  385. ResetSelection(true);
  386. }
  387. void MoveAllForwardHandler(object sender, ExecutedRoutedEventArgs e)
  388. {
  389. ResetSelection(false);
  390. }
  391. void MoveBackHandler(object sender, ExecutedRoutedEventArgs e)
  392. {
  393. MoveSelection(true);
  394. }
  395. void MoveForwardHandler(object sender, ExecutedRoutedEventArgs e)
  396. {
  397. MoveSelection(false);
  398. }
  399. private static void MoveThumb(FrameworkElement x, FrameworkElement y, double horizonalChange,
  400. Orientation orientation)
  401. {
  402. double change;
  403. if (orientation == Orientation.Horizontal)
  404. {
  405. if (!Double.IsNaN(x.Width) && !Double.IsNaN(y.Width))
  406. {
  407. if (horizonalChange < 0) //slider went left
  408. {
  409. change = GetChangeKeepPositive(x.Width, horizonalChange);
  410. if (x.Name == "PART_MiddleThumb")
  411. {
  412. if (x.Width > x.MinWidth)
  413. {
  414. if (x.Width + change < x.MinWidth)
  415. {
  416. double dif = x.Width - x.MinWidth;
  417. x.Width = x.MinWidth;
  418. y.Width += dif;
  419. }
  420. else
  421. {
  422. x.Width += change;
  423. y.Width -= change;
  424. }
  425. }
  426. }
  427. else
  428. {
  429. x.Width += change;
  430. y.Width -= change;
  431. }
  432. }
  433. else if (horizonalChange > 0) //slider went right if(horizontal change == 0 do nothing)
  434. {
  435. change = -GetChangeKeepPositive(y.Width, -horizonalChange);
  436. if (y.Name == "PART_MiddleThumb")
  437. {
  438. if (y.Width > y.MinWidth)
  439. {
  440. if (y.Width - change < y.MinWidth)
  441. {
  442. double dif = y.Width - y.MinWidth;
  443. y.Width = y.MinWidth;
  444. x.Width += dif;
  445. }
  446. else
  447. {
  448. x.Width += change;
  449. y.Width -= change;
  450. }
  451. }
  452. }
  453. else
  454. {
  455. x.Width += change;
  456. y.Width -= change;
  457. }
  458. }
  459. }
  460. }
  461. else if (orientation == Orientation.Vertical)
  462. {
  463. if (!Double.IsNaN(x.Height) && !Double.IsNaN(y.Height))
  464. {
  465. if (horizonalChange < 0) //slider went up
  466. {
  467. change = -GetChangeKeepPositive(y.Height, horizonalChange);//get positive number
  468. if (y.Name == "PART_MiddleThumb")
  469. {
  470. if (y.Height > y.MinHeight)
  471. {
  472. if (y.Height - change < y.MinHeight)
  473. {
  474. double dif = y.Height - y.MinHeight;
  475. y.Height = y.MinHeight;
  476. x.Height += dif;
  477. }
  478. else
  479. {
  480. x.Height += change;
  481. y.Height -= change;
  482. }
  483. }
  484. }
  485. else
  486. {
  487. x.Height += change;
  488. y.Height -= change;
  489. }
  490. }
  491. else if (horizonalChange > 0) //slider went down if(horizontal change == 0 do nothing)
  492. {
  493. change = GetChangeKeepPositive(x.Height, -horizonalChange);//get negative number
  494. if (x.Name == "PART_MiddleThumb")
  495. {
  496. if (x.Height > y.MinHeight)
  497. {
  498. if (x.Height + change < x.MinHeight)
  499. {
  500. double dif = x.Height - x.MinHeight;
  501. x.Height = x.MinHeight;
  502. y.Height += dif;
  503. }
  504. else
  505. {
  506. x.Height += change;
  507. y.Height -= change;
  508. }
  509. }
  510. }
  511. else
  512. {
  513. x.Height += change;
  514. y.Height -= change;
  515. }
  516. }
  517. }
  518. }
  519. }
  520. private static void MoveThumb(FrameworkElement x, FrameworkElement y, double horizonalChange,
  521. Orientation orientation, out Direction direction)
  522. {
  523. double change;
  524. direction = Direction.Increase;
  525. if (orientation == Orientation.Horizontal)
  526. {
  527. if (!Double.IsNaN(x.Width) && !Double.IsNaN(y.Width))
  528. {
  529. if (horizonalChange < 0) //slider went left
  530. {
  531. direction = Direction.Decrease;
  532. change = GetChangeKeepPositive(x.Width, horizonalChange);
  533. if (x.Name == "PART_MiddleThumb")
  534. {
  535. if (x.Width > x.MinWidth)
  536. {
  537. if (x.Width + change < x.MinWidth)
  538. {
  539. double dif = x.Width - x.MinWidth;
  540. x.Width = x.MinWidth;
  541. y.Width += dif;
  542. }
  543. else
  544. {
  545. x.Width += change;
  546. y.Width -= change;
  547. }
  548. }
  549. }
  550. else
  551. {
  552. x.Width += change;
  553. y.Width -= change;
  554. }
  555. }
  556. else if (horizonalChange > 0) //slider went right if(horizontal change == 0 do nothing)
  557. {
  558. direction = Direction.Increase;
  559. change = -GetChangeKeepPositive(y.Width, -horizonalChange);
  560. if (y.Name == "PART_MiddleThumb")
  561. {
  562. if (y.Width > y.MinWidth)
  563. {
  564. if (y.Width - change < y.MinWidth)
  565. {
  566. double dif = y.Width - y.MinWidth;
  567. y.Width = y.MinWidth;
  568. x.Width += dif;
  569. }
  570. else
  571. {
  572. x.Width += change;
  573. y.Width -= change;
  574. }
  575. }
  576. }
  577. else
  578. {
  579. x.Width += change;
  580. y.Width -= change;
  581. }
  582. }
  583. }
  584. }
  585. else
  586. {
  587. if (!Double.IsNaN(x.Height) && !Double.IsNaN(y.Height))
  588. {
  589. if (horizonalChange < 0) //slider went up
  590. {
  591. direction = Direction.Increase;
  592. change = -GetChangeKeepPositive(y.Height, horizonalChange);//get positive number
  593. if (y.Name == "PART_MiddleThumb")
  594. {
  595. if (y.Height > y.MinHeight)
  596. {
  597. if (y.Height - change < y.MinHeight)
  598. {
  599. double dif = y.Height - y.MinHeight;
  600. y.Height = y.MinHeight;
  601. x.Height += dif;
  602. }
  603. else
  604. {
  605. x.Height += change;
  606. y.Height -= change;
  607. }
  608. }
  609. }
  610. else
  611. {
  612. x.Height += change;
  613. y.Height -= change;
  614. }
  615. }
  616. else if (horizonalChange > 0) //slider went down if(horizontal change == 0 do nothing)
  617. {
  618. direction = Direction.Decrease;
  619. change = GetChangeKeepPositive(x.Height, -horizonalChange);//get negative number
  620. if (x.Name == "PART_MiddleThumb")
  621. {
  622. if (x.Height > y.MinHeight)
  623. {
  624. if (x.Height + change < x.MinHeight)
  625. {
  626. double dif = x.Height - x.MinHeight;
  627. x.Height = x.MinHeight;
  628. y.Height += dif;
  629. }
  630. else
  631. {
  632. x.Height += change;
  633. y.Height -= change;
  634. }
  635. }
  636. }
  637. else
  638. {
  639. x.Height += change;
  640. y.Height -= change;
  641. }
  642. }
  643. }
  644. }
  645. }
  646. //Recalculation of Control Height or Width
  647. private void ReCalculateSize()
  648. {
  649. if (_leftButton != null && _rightButton != null && _centerThumb != null)
  650. {
  651. if (Orientation == Orientation.Horizontal)
  652. {
  653. _movableWidth =
  654. Math.Max(
  655. ActualWidth - _rightThumb.ActualWidth - _leftThumb.ActualWidth - MinRangeWidth, 1);
  656. if (MovableRange <= 0)
  657. {
  658. _leftButton.Width = Double.NaN;
  659. _rightButton.Width = Double.NaN;
  660. }
  661. else
  662. {
  663. _leftButton.Width = Math.Max(_movableWidth * (LowerValue - Minimum) / MovableRange, 0);
  664. _rightButton.Width = Math.Max(_movableWidth * (Maximum - UpperValue) / MovableRange, 0);
  665. }
  666. if (IsValidDouble(_rightButton.Width) && IsValidDouble(_leftButton.Width))
  667. {
  668. _centerThumb.Width =
  669. Math.Max(
  670. ActualWidth - (_leftButton.Width + _rightButton.Width + _rightThumb.ActualWidth +
  671. _leftThumb.ActualWidth), 0);
  672. }
  673. else
  674. {
  675. _centerThumb.Width =
  676. Math.Max(
  677. ActualWidth - (_rightThumb.ActualWidth + _leftThumb.ActualWidth), 0);
  678. }
  679. }
  680. else if (Orientation == Orientation.Vertical)
  681. {
  682. _movableWidth =
  683. Math.Max(
  684. ActualHeight - _rightThumb.ActualHeight - _leftThumb.ActualHeight - MinRangeWidth, 1);
  685. if (MovableRange <= 0)
  686. {
  687. _leftButton.Height = Double.NaN;
  688. _rightButton.Height = Double.NaN;
  689. }
  690. else
  691. {
  692. _leftButton.Height = Math.Max(_movableWidth * (LowerValue - Minimum) / MovableRange, 0);
  693. _rightButton.Height = Math.Max(_movableWidth * (Maximum - UpperValue) / MovableRange, 0);
  694. }
  695. if (IsValidDouble(_rightButton.Height) && IsValidDouble(_leftButton.Height))
  696. {
  697. _centerThumb.Height =
  698. Math.Max(
  699. ActualHeight - (_leftButton.Height + _rightButton.Height + _rightThumb.ActualHeight +
  700. _leftThumb.ActualHeight), 0);
  701. }
  702. else
  703. {
  704. _centerThumb.Height =
  705. Math.Max(
  706. ActualHeight - (_rightThumb.ActualHeight + _leftThumb.ActualHeight), 0);
  707. }
  708. }
  709. _density = _movableWidth / MovableRange;
  710. }
  711. }
  712. /*
  713. //private void ReCalculateRangeSelected(bool reCalculateLowerValue, bool reCalculateUpperValue)
  714. //{
  715. // _internalUpdate = true; //set flag to signal that the properties are being set by the object itself
  716. // if (reCalculateLowerValue)
  717. // {
  718. // _oldLower = LowerValue;
  719. // double width = Orientation == Orientation.Horizontal ? _leftButton.Width : _leftButton.Height;
  720. // //Check first if button width is not Double.NaN
  721. // if (IsValidDouble(width))
  722. // {
  723. // // Make sure to get exactly rangestart if thumb is at the start
  724. // double lower = Equals(width, 0.0)
  725. // ? Minimum
  726. // : Math.Max(Minimum, (Minimum + MovableRange * width / _movableWidth));
  727. // if (!_isMoved)
  728. // {
  729. // LowerValue = _roundToPrecision ? Math.Round(lower, _precision) : lower;
  730. // }
  731. // else
  732. // {
  733. // LowerValue = lower;
  734. // }
  735. // }
  736. // }
  737. // if (reCalculateUpperValue)
  738. // {
  739. // _oldUpper = UpperValue;
  740. // double width = Orientation == Orientation.Horizontal ? _rightButton.Width : _rightButton.Height;
  741. // //Check first if button width is not Double.NaN
  742. // if (IsValidDouble(width))
  743. // {
  744. // // Make sure to get exactly rangestop if thumb is at the end
  745. // double upper = Equals(width, 0.0)
  746. // ? Maximum
  747. // : Math.Min(Maximum, (Maximum - MovableRange * width / _movableWidth));
  748. // if (!_isMoved)
  749. // {
  750. // UpperValue = _roundToPrecision
  751. // ? Math.Round(upper, _precision)
  752. // : upper;
  753. // }
  754. // else
  755. // {
  756. // UpperValue = upper;
  757. // }
  758. // }
  759. // }
  760. // _roundToPrecision = false;
  761. // _internalUpdate = false; //set flag to signal that the properties are being set by the object itself
  762. // if (reCalculateLowerValue || reCalculateUpperValue)
  763. // {
  764. // if (!Equals(_oldLower, LowerValue) || !Equals(_oldUpper, UpperValue))
  765. // {
  766. // //raise the RangeSelectionChanged event
  767. // OnRangeSelectionChanged(new RangeSelectionChangedEventArgs(LowerValue, UpperValue, _oldLower,
  768. // _oldUpper));
  769. // }
  770. // }
  771. // if (reCalculateLowerValue && !Equals(_oldLower, LowerValue))
  772. // {
  773. // OnRangeParameterChanged(
  774. // new RangeParameterChangedEventArgs(RangeParameterChangeType.Lower, _oldLower, LowerValue),
  775. // LowerValueChangedEvent);
  776. // }
  777. // if (reCalculateUpperValue && !Equals(_oldUpper, UpperValue))
  778. // {
  779. // OnRangeParameterChanged(
  780. // new RangeParameterChangedEventArgs(RangeParameterChangeType.Upper, _oldUpper, UpperValue),
  781. // UpperValueChangedEvent);
  782. // }
  783. //}
  784. */
  785. //Method calculates new values when IsSnapToTickEnabled = FALSE
  786. private void ReCalculateRangeSelected(bool reCalculateLowerValue, bool reCalculateUpperValue, Direction direction)
  787. {
  788. _internalUpdate = true; //set flag to signal that the properties are being set by the object itself
  789. if (direction == Direction.Increase)
  790. {
  791. if (reCalculateUpperValue)
  792. {
  793. _oldUpper = UpperValue;
  794. double width = Orientation == Orientation.Horizontal ? _rightButton.Width : _rightButton.Height;
  795. //Check first if button width is not Double.NaN
  796. if (IsValidDouble(width))
  797. {
  798. // Make sure to get exactly rangestop if thumb is at the end
  799. double upper = Equals(width, 0.0)
  800. ? Maximum
  801. : Math.Min(Maximum, (Maximum - MovableRange * width / _movableWidth));
  802. if (!_isMoved)
  803. {
  804. UpperValue = _roundToPrecision
  805. ? Math.Round(upper, _precision)
  806. : upper;
  807. }
  808. else
  809. {
  810. UpperValue = upper;
  811. }
  812. }
  813. }
  814. if (reCalculateLowerValue)
  815. {
  816. _oldLower = LowerValue;
  817. double width = Orientation == Orientation.Horizontal ? _leftButton.Width : _leftButton.Height;
  818. //Check first if button width is not Double.NaN
  819. if (IsValidDouble(width))
  820. {
  821. // Make sure to get exactly rangestart if thumb is at the start
  822. double lower = Equals(width, 0.0)
  823. ? Minimum
  824. : Math.Max(Minimum, (Minimum + MovableRange*width/_movableWidth));
  825. if (!_isMoved)
  826. {
  827. LowerValue = _roundToPrecision ? Math.Round(lower, _precision) : lower;
  828. }
  829. else
  830. {
  831. LowerValue = lower;
  832. }
  833. }
  834. }
  835. }
  836. else
  837. {
  838. if (reCalculateLowerValue)
  839. {
  840. _oldLower = LowerValue;
  841. double width = Orientation == Orientation.Horizontal ? _leftButton.Width : _leftButton.Height;
  842. //Check first if button width is not Double.NaN
  843. if (IsValidDouble(width))
  844. {
  845. // Make sure to get exactly rangestart if thumb is at the start
  846. double lower = Equals(width, 0.0)
  847. ? Minimum
  848. : Math.Max(Minimum, (Minimum + MovableRange * width / _movableWidth));
  849. if (!_isMoved)
  850. {
  851. LowerValue = _roundToPrecision ? Math.Round(lower, _precision) : lower;
  852. }
  853. else
  854. {
  855. LowerValue = lower;
  856. }
  857. }
  858. }
  859. if (reCalculateUpperValue)
  860. {
  861. _oldUpper = UpperValue;
  862. double width = Orientation == Orientation.Horizontal ? _rightButton.Width : _rightButton.Height;
  863. //Check first if button width is not Double.NaN
  864. if (IsValidDouble(width))
  865. {
  866. // Make sure to get exactly rangestop if thumb is at the end
  867. double upper = Equals(width, 0.0)
  868. ? Maximum
  869. : Math.Min(Maximum, (Maximum - MovableRange * width / _movableWidth));
  870. if (!_isMoved)
  871. {
  872. UpperValue = _roundToPrecision
  873. ? Math.Round(upper, _precision)
  874. : upper;
  875. }
  876. else
  877. {
  878. UpperValue = upper;
  879. }
  880. }
  881. }
  882. }
  883. _roundToPrecision = false;
  884. _internalUpdate = false; //set flag to signal that the properties are being set by the object itself
  885. if (reCalculateLowerValue || reCalculateUpperValue)
  886. {
  887. if (!Equals(_oldLower, LowerValue) || !Equals(_oldUpper, UpperValue))
  888. {
  889. //raise the RangeSelectionChanged event
  890. OnRangeSelectionChanged(new RangeSelectionChangedEventArgs(LowerValue, UpperValue, _oldLower,
  891. _oldUpper));
  892. }
  893. }
  894. if (reCalculateLowerValue && !Equals(_oldLower, LowerValue))
  895. {
  896. OnRangeParameterChanged(
  897. new RangeParameterChangedEventArgs(RangeParameterChangeType.Lower, _oldLower, LowerValue),
  898. LowerValueChangedEvent);
  899. }
  900. if (reCalculateUpperValue && !Equals(_oldUpper, UpperValue))
  901. {
  902. OnRangeParameterChanged(
  903. new RangeParameterChangedEventArgs(RangeParameterChangeType.Upper, _oldUpper, UpperValue),
  904. UpperValueChangedEvent);
  905. }
  906. }
  907. //Method used for cheking and setting correct values when IsSnapToTickEnable = TRUE (When thumb moving separately)
  908. private void ReCalculateRangeSelected(bool reCalculateLowerValue, bool reCalculateUpperValue, double value, Direction direction)
  909. {
  910. _internalUpdate = true; //set flag to signal that the properties are being set by the object itself
  911. if (reCalculateLowerValue)
  912. {
  913. _oldLower = LowerValue;
  914. double lower = 0;
  915. if (IsSnapToTickEnabled)
  916. {
  917. if (direction == Direction.Increase)
  918. {
  919. lower = Math.Min(UpperValue - MinRange, value);
  920. }
  921. else
  922. {
  923. lower = Math.Max(Minimum, value);
  924. }
  925. }
  926. if (!TickFrequency.ToString(CultureInfo.InvariantCulture).ToLower().Contains("e+") &&
  927. TickFrequency.ToString(CultureInfo.InvariantCulture).Contains("."))
  928. {
  929. //decimal part is for cutting value exactly on that number of digits, which has TickFrequency to have correct values
  930. String[] decimalPart = TickFrequency.ToString(CultureInfo.InvariantCulture).Split('.');
  931. LowerValue = Math.Round(lower, decimalPart[1].Length, MidpointRounding.AwayFromZero);
  932. }
  933. else
  934. {
  935. LowerValue = lower;
  936. }
  937. }
  938. if (reCalculateUpperValue)
  939. {
  940. _oldUpper = UpperValue;
  941. double upper = 0;
  942. if (IsSnapToTickEnabled)
  943. {
  944. if (direction == Direction.Increase)
  945. {
  946. upper = Math.Min(value, Maximum);
  947. }
  948. else
  949. {
  950. upper = Math.Max(LowerValue + MinRange, value);
  951. }
  952. }
  953. if (!TickFrequency.ToString(CultureInfo.InvariantCulture).ToLower().Contains("e+") &&
  954. TickFrequency.ToString(CultureInfo.InvariantCulture).Contains("."))
  955. {
  956. String[] decimalPart = TickFrequency.ToString(CultureInfo.InvariantCulture).Split('.');
  957. UpperValue = Math.Round(upper, decimalPart[1].Length, MidpointRounding.AwayFromZero);
  958. }
  959. else
  960. {
  961. UpperValue = upper;
  962. }
  963. }
  964. _internalUpdate = false; //set flag to signal that the properties are being set by the object itself
  965. if (reCalculateLowerValue || reCalculateUpperValue)
  966. {
  967. if (!Equals(_oldLower, LowerValue) || !Equals(_oldUpper, UpperValue))
  968. {
  969. //raise the RangeSelectionChanged event
  970. OnRangeSelectionChanged(new RangeSelectionChangedEventArgs(LowerValue, UpperValue, _oldLower,
  971. _oldUpper));
  972. }
  973. }
  974. if (reCalculateLowerValue && !Equals(_oldLower, LowerValue))
  975. {
  976. OnRangeParameterChanged(
  977. new RangeParameterChangedEventArgs(RangeParameterChangeType.Lower, _oldLower, LowerValue),
  978. LowerValueChangedEvent);
  979. }
  980. if (reCalculateUpperValue && !Equals(_oldUpper, UpperValue))
  981. {
  982. OnRangeParameterChanged(
  983. new RangeParameterChangedEventArgs(RangeParameterChangeType.Upper, _oldUpper, UpperValue),
  984. UpperValueChangedEvent);
  985. }
  986. }
  987. //Method used for cheking and setting correct values when IsSnapToTickEnable = TRUE (When thumb moving together)
  988. private void ReCalculateRangeSelected(double newLower, double newUpper, Direction direction)
  989. {
  990. double lower = 0, upper = 0;
  991. _internalUpdate = true; //set flag to signal that the properties are being set by the object itself
  992. _oldLower = LowerValue;
  993. _oldUpper = UpperValue;
  994. if (IsSnapToTickEnabled)
  995. {
  996. if (direction == Direction.Increase)
  997. {
  998. lower = Math.Min(newLower, Maximum-(UpperValue - LowerValue));
  999. upper = Math.Min(newUpper, Maximum);
  1000. }
  1001. else
  1002. {
  1003. lower = Math.Max(newLower, Minimum);
  1004. upper = Math.Max(Minimum+(UpperValue - LowerValue), newUpper);
  1005. }
  1006. if (!TickFrequency.ToString(CultureInfo.InvariantCulture).ToLower().Contains("e+") &&
  1007. TickFrequency.ToString(CultureInfo.InvariantCulture).Contains("."))
  1008. {
  1009. //decimal part is for cutting value exactly on that number of digits, which has TickFrequency to have correct values
  1010. String[] decimalPart = TickFrequency.ToString(CultureInfo.InvariantCulture).Split('.');
  1011. //used when whole range decreasing to have correct updated values (lower first, upper - second)
  1012. if (direction == Direction.Decrease)
  1013. {
  1014. LowerValue = Math.Round(lower, decimalPart[1].Length, MidpointRounding.AwayFromZero);
  1015. UpperValue = Math.Round(upper, decimalPart[1].Length, MidpointRounding.AwayFromZero);
  1016. }
  1017. //used when whole range increasing to have correct updated values (upper first, lower - second)
  1018. else
  1019. {
  1020. UpperValue = Math.Round(upper, decimalPart[1].Length, MidpointRounding.AwayFromZero);
  1021. LowerValue = Math.Round(lower, decimalPart[1].Length, MidpointRounding.AwayFromZero);
  1022. }
  1023. }
  1024. else
  1025. {
  1026. //used when whole range decreasing to have correct updated values (lower first, upper - second)
  1027. if (direction == Direction.Decrease)
  1028. {
  1029. LowerValue = lower;
  1030. UpperValue = upper;
  1031. }
  1032. //used when whole range increasing to have correct updated values (upper first, lower - second)
  1033. else
  1034. {
  1035. UpperValue = upper;
  1036. LowerValue = lower;
  1037. }
  1038. }
  1039. }
  1040. _internalUpdate = false; //set flag to signal that the properties are being set by the object itself
  1041. if (!Equals(_oldLower, LowerValue) || !Equals(_oldUpper, UpperValue))
  1042. {
  1043. //raise the RangeSelectionChanged event
  1044. OnRangeSelectionChanged(new RangeSelectionChangedEventArgs(LowerValue, UpperValue, _oldLower, _oldUpper));
  1045. }
  1046. if (!Equals(_oldLower, LowerValue))
  1047. {
  1048. OnRangeParameterChanged(
  1049. new RangeParameterChangedEventArgs(RangeParameterChangeType.Lower, _oldLower, LowerValue),
  1050. LowerValueChangedEvent);
  1051. }
  1052. if (!Equals(_oldUpper, UpperValue))
  1053. {
  1054. OnRangeParameterChanged(
  1055. new RangeParameterChangedEventArgs(RangeParameterChangeType.Upper, _oldUpper, UpperValue),
  1056. UpperValueChangedEvent);
  1057. }
  1058. }
  1059. private void OnRangeParameterChanged(RangeParameterChangedEventArgs e, RoutedEvent Event)
  1060. {
  1061. e.RoutedEvent = Event;
  1062. RaiseEvent(e);
  1063. }
  1064. public void MoveSelection(bool isLeft)
  1065. {
  1066. double widthChange = SmallChange * (UpperValue - LowerValue)
  1067. * _movableWidth / MovableRange;
  1068. widthChange = isLeft ? -widthChange : widthChange;
  1069. MoveThumb(_leftButton, _rightButton, widthChange, Orientation, out _direction);
  1070. ReCalculateRangeSelected(true, true, _direction);
  1071. }
  1072. public void ResetSelection(bool isStart)
  1073. {
  1074. double widthChange = Maximum - Minimum;
  1075. widthChange = isStart ? -widthChange : widthChange;
  1076. MoveThumb(_leftButton, _rightButton, widthChange, Orientation, out _direction);
  1077. ReCalculateRangeSelected(true, true, _direction);
  1078. }
  1079. private void OnRangeSelectionChanged(RangeSelectionChangedEventArgs e)
  1080. {
  1081. e.RoutedEvent = RangeSelectionChangedEvent;
  1082. RaiseEvent(e);
  1083. }
  1084. public override void OnApplyTemplate()
  1085. {
  1086. base.OnApplyTemplate();
  1087. _container = EnforceInstance<StackPanel>("PART_Container");
  1088. _visualElementsContainer = EnforceInstance<StackPanel>("PART_RangeSliderContainer");
  1089. _centerThumb = EnforceInstance<Thumb>("PART_MiddleThumb");
  1090. _leftButton = EnforceInstance<RepeatButton>("PART_LeftEdge");
  1091. _rightButton = EnforceInstance<RepeatButton>("PART_RightEdge");
  1092. _leftThumb = EnforceInstance<Thumb>("PART_LeftThumb");
  1093. _rightThumb = EnforceInstance<Thumb>("PART_RightThumb");
  1094. InitializeVisualElementsContainer();
  1095. ReCalculateSize();
  1096. }
  1097. //Get element from name. If it exist then element instance return, if not, new will be created
  1098. T EnforceInstance<T>(string partName) where T : FrameworkElement, new()
  1099. {
  1100. T element = GetTemplateChild(partName) as T ?? new T();
  1101. return element;
  1102. }
  1103. //adds visual element to the container
  1104. private void InitializeVisualElementsContainer()
  1105. {
  1106. _leftThumb.DragCompleted += LeftThumbDragComplete;
  1107. _rightThumb.DragCompleted += RightThumbDragComplete;
  1108. _leftThumb.DragStarted += LeftThumbDragStart;
  1109. _rightThumb.DragStarted += RightThumbDragStart;
  1110. _centerThumb.DragStarted += CenterThumbDragStarted;
  1111. _centerThumb.DragCompleted += CenterThumbDragCompleted;
  1112. //handle the drag delta events
  1113. _centerThumb.DragDelta += CenterThumbDragDelta;
  1114. _leftThumb.DragDelta += LeftThumbDragDelta;
  1115. _rightThumb.DragDelta += RightThumbDragDelta;
  1116. _visualElementsContainer.PreviewMouseDown += VisualElementsContainerPreviewMouseDown;
  1117. _visualElementsContainer.PreviewMouseUp += VisualElementsContainerPreviewMouseUp;
  1118. _visualElementsContainer.MouseLeave += VisualElementsContainerMouseLeave;
  1119. _visualElementsContainer.MouseDown += VisualElementsContainerMouseDown;
  1120. }
  1121. //Handler for preview mouse button down for the whole StackPanel container
  1122. void VisualElementsContainerPreviewMouseDown(object sender, MouseButtonEventArgs e)
  1123. {
  1124. Point position = Mouse.GetPosition(_visualElementsContainer);
  1125. if (Orientation == Orientation.Horizontal)
  1126. {
  1127. if (position.X < _leftButton.ActualWidth)
  1128. {
  1129. LeftButtonMouseDown();
  1130. }
  1131. else if (position.X > ActualWidth - _rightButton.ActualWidth)
  1132. {
  1133. RightButtonMouseDown();
  1134. }
  1135. else if (position.X > (_leftButton.ActualWidth + _leftThumb.ActualWidth) &&
  1136. position.X < (ActualWidth - (_rightButton.ActualWidth + _rightThumb.ActualWidth)))
  1137. {
  1138. CentralThumbMouseDown();
  1139. }
  1140. }
  1141. else
  1142. {
  1143. if (position.Y > ActualHeight - _leftButton.ActualHeight)
  1144. {
  1145. LeftButtonMouseDown();
  1146. }
  1147. else if (position.Y < _rightButton.ActualHeight)
  1148. {
  1149. RightButtonMouseDown();
  1150. }
  1151. else if (position.Y > (_rightButton.ActualHeight + _rightButton.ActualHeight) &&
  1152. position.Y < (ActualHeight - (_leftButton.ActualHeight + _leftThumb.ActualHeight)))
  1153. {
  1154. CentralThumbMouseDown();
  1155. }
  1156. }
  1157. }
  1158. void VisualElementsContainerMouseDown(object sender, MouseButtonEventArgs e)
  1159. {
  1160. if (e.MiddleButton == MouseButtonState.Pressed)
  1161. {
  1162. MoveWholeRange = MoveWholeRange != true;
  1163. }
  1164. }
  1165. #region Mouse events
  1166. private void VisualElementsContainerMouseLeave(object sender, MouseEventArgs e)
  1167. {
  1168. _tickCount = 0;
  1169. _timer.Stop();
  1170. }
  1171. private void VisualElementsContainerPreviewMouseUp(object sender, MouseButtonEventArgs e)
  1172. {
  1173. _tickCount = 0;
  1174. _timer.Stop();
  1175. _centerThumbBlocked = false;
  1176. }
  1177. private void LeftButtonMouseDown()
  1178. {
  1179. if (Mouse.LeftButton == MouseButtonState.Pressed)
  1180. {
  1181. Point p = Mouse.GetPosition(_visualElementsContainer);
  1182. double change = Orientation == Orientation.Horizontal
  1183. ? _leftButton.ActualWidth - p.X + (_leftThumb.ActualWidth / 2)
  1184. : -(_leftButton.ActualHeight - (ActualHeight - (p.Y + (_leftThumb.ActualHeight / 2))));
  1185. if (!IsSnapToTickEnabled)
  1186. {
  1187. if (IsMoveToPointEnabled && !MoveWholeRange)
  1188. {
  1189. MoveThumb(_leftButton, _centerThumb, -change, Orientation, out _direction);
  1190. ReCalculateRangeSelected(true, false, _direction);
  1191. }
  1192. else if (IsMoveToPointEnabled && MoveWholeRange)
  1193. {
  1194. MoveThumb(_leftButton, _rightButton, -change, Orientation, out _direction);
  1195. ReCalculateRangeSelected(true, true, _direction);
  1196. }
  1197. }
  1198. else
  1199. {
  1200. if (IsMoveToPointEnabled && !MoveWholeRange)
  1201. {
  1202. JumpToNextTick(Direction.Decrease, ButtonType.BottomLeft, -change, LowerValue, true);
  1203. }
  1204. else if (IsMoveToPointEnabled && MoveWholeRange)
  1205. {
  1206. JumpToNextTick(Direction.Decrease, ButtonType.Both, -change, LowerValue, true);
  1207. }
  1208. }
  1209. if (!IsMoveToPointEnabled)
  1210. {
  1211. _position = Mouse.GetPosition(_visualElementsContainer);
  1212. _bType = MoveWholeRange ? ButtonType.Both : ButtonType.BottomLeft;
  1213. _currentpoint = Orientation == Orientation.Horizontal ? _position.X : _position.Y;
  1214. _currenValue = LowerValue;
  1215. _isInsideRange = false;
  1216. _direction = Direction.Decrease;
  1217. _timer.Start();
  1218. }
  1219. }
  1220. }
  1221. private void RightButtonMouseDown()
  1222. {
  1223. if (Mouse.LeftButton == MouseButtonState.Pressed)
  1224. {
  1225. Point p = Mouse.GetPosition(_visualElementsContainer);
  1226. double change = Orientation == Orientation.Horizontal
  1227. ? _rightButton.ActualWidth - (ActualWidth - (p.X + (_rightThumb.ActualWidth / 2)))
  1228. : -(_rightButton.ActualHeight - (p.Y - (_rightThumb.ActualHeight / 2)));
  1229. if (!IsSnapToTickEnabled)
  1230. {
  1231. if (IsMoveToPointEnabled && !MoveWholeRange)
  1232. {
  1233. MoveThumb(_centerThumb, _rightButton, change, Orientation, out _direction);
  1234. ReCalculateRangeSelected(false, true, _direction);
  1235. }
  1236. else if (IsMoveToPointEnabled && MoveWholeRange)
  1237. {
  1238. MoveThumb(_leftButton, _rightButton, change, Orientation, out _direction);
  1239. ReCalculateRangeSelected(true, true, _direction);
  1240. }
  1241. }
  1242. else
  1243. {
  1244. if (IsMoveToPointEnabled && !MoveWholeRange)
  1245. {
  1246. JumpToNextTick(Direction.Increase, ButtonType.TopRight, change, UpperValue, true);
  1247. }
  1248. else if (IsMoveToPointEnabled && MoveWholeRange)
  1249. {
  1250. JumpToNextTick(Direction.Increase, ButtonType.Both, change, UpperValue, true);
  1251. }
  1252. }
  1253. if (!IsMoveToPointEnabled)
  1254. {
  1255. _position = Mouse.GetPosition(_visualElementsContainer);
  1256. _bType = MoveWholeRange ? ButtonType.Both : ButtonType.TopRight;
  1257. _currentpoint = Orientation == Orientation.Horizontal ? _position.X : _position.Y;
  1258. _currenValue = UpperValue;
  1259. _direction = Direction.Increase;
  1260. _isInsideRange = false;
  1261. _timer.Start();
  1262. }
  1263. }
  1264. }
  1265. private void CentralThumbMouseDown()
  1266. {
  1267. if (ExtendedMode)
  1268. {
  1269. if (Mouse.LeftButton == MouseButtonState.Pressed &&
  1270. (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
  1271. {
  1272. _centerThumbBlocked = true;
  1273. Point p = Mouse.GetPosition(_visualElementsContainer);
  1274. double change = Orientation == Orientation.Horizontal
  1275. ? (p.X + (_leftThumb.ActualWidth / 2) - (_leftButton.ActualWidth + _leftThumb.ActualWidth))
  1276. : -(ActualHeight - ((p.Y + (_leftThumb.ActualHeight / 2)) + _leftButton.ActualHeight));
  1277. if (!IsSnapToTickEnabled)
  1278. {
  1279. if (IsMoveToPointEnabled && !MoveWholeRange)
  1280. {
  1281. MoveThumb(_leftButton, _centerThumb, change, Orientation, out _direction);
  1282. ReCalculateRangeSelected(true, false, _direction);
  1283. }
  1284. else if (IsMoveToPointEnabled && MoveWholeRange)
  1285. {
  1286. MoveThumb(_leftButton, _rightButton, change, Orientation, out _direction);
  1287. ReCalculateRangeSelected(true, true, _direction);
  1288. }
  1289. }
  1290. else
  1291. {
  1292. if (IsMoveToPointEnabled && !MoveWholeRange)
  1293. {
  1294. JumpToNextTick(Direction.Increase, ButtonType.BottomLeft, change, LowerValue, true);
  1295. }
  1296. else if (IsMoveToPointEnabled && MoveWholeRange)
  1297. {
  1298. JumpToNextTick(Direction.Increase, ButtonType.Both, change, LowerValue, true);
  1299. }
  1300. }
  1301. if (!IsMoveToPointEnabled)
  1302. {
  1303. _position = Mouse.GetPosition(_visualElementsContainer);
  1304. _bType = MoveWholeRange ? ButtonType.Both : ButtonType.BottomLeft;
  1305. _currentpoint = Orientation == Orientation.Horizontal ? _position.X : _position.Y;
  1306. _currenValue = LowerValue;
  1307. _direction = Direction.Increase;
  1308. _isInsideRange = true;
  1309. _timer.Start();
  1310. }
  1311. }
  1312. else if (Mouse.RightButton == MouseButtonState.Pressed &&
  1313. (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
  1314. {
  1315. _centerThumbBlocked = true;
  1316. Point p = Mouse.GetPosition(_visualElementsContainer);
  1317. double change = Orientation == Orientation.Horizontal
  1318. ? ActualWidth - (p.X + (_rightThumb.ActualWidth / 2) + _rightButton.ActualWidth)
  1319. : -(p.Y + (_rightThumb.ActualHeight / 2) - (_rightButton.ActualHeight + _rightThumb.ActualHeight));
  1320. if (!IsSnapToTickEnabled)
  1321. {
  1322. if (IsMoveToPointEnabled && !MoveWholeRange)
  1323. {
  1324. MoveThumb(_centerThumb, _rightButton, -change, Orientation, out _direction);
  1325. ReCalculateRangeSelected(false, true, _direction);
  1326. }
  1327. else if (IsMoveToPointEnabled && MoveWholeRange)
  1328. {
  1329. MoveThumb(_leftButton, _rightButton, -change, Orientation, out _direction);
  1330. ReCalculateRangeSelected(true, true, _direction);
  1331. }
  1332. }
  1333. else
  1334. {
  1335. if (IsMoveToPointEnabled && !MoveWholeRange)
  1336. {
  1337. JumpToNextTick(Direction.Decrease, ButtonType.TopRight, -change, UpperValue, true);
  1338. }
  1339. else if (IsMoveToPointEnabled && MoveWholeRange)
  1340. {
  1341. JumpToNextTick(Direction.Decrease, ButtonType.Both, -change, UpperValue, true);
  1342. }
  1343. }
  1344. if (!IsMoveToPointEnabled)
  1345. {
  1346. _position = Mouse.GetPosition(_visualElementsContainer);
  1347. _bType = MoveWholeRange ? ButtonType.Both : ButtonType.TopRight;
  1348. _currentpoint = Orientation == Orientation.Horizontal ? _position.X : _position.Y;
  1349. _currenValue = UpperValue;
  1350. _direction = Direction.Decrease;
  1351. _isInsideRange = true;
  1352. _timer.Start();
  1353. }
  1354. }
  1355. }
  1356. }
  1357. #endregion
  1358. #region Thumb Drag event handlers
  1359. private void LeftThumbDragStart(object sender, DragStartedEventArgs e)
  1360. {
  1361. _isMoved = true;
  1362. if (AutoToolTipPlacement != AutoToolTipPlacement.None)
  1363. {
  1364. if (_autoToolTip == null)
  1365. {
  1366. _autoToolTip = new ToolTip();
  1367. _autoToolTip.Placement = PlacementMode.Custom;
  1368. _autoToolTip.CustomPopupPlacementCallback = PopupPlacementCallback;
  1369. }
  1370. _autoToolTip.Content = GetLowerToolTipNumber();
  1371. _autoToolTip.PlacementTarget = _leftThumb;
  1372. _autoToolTip.IsOpen = true;
  1373. }
  1374. _basePoint = Mouse.GetPosition(_container);
  1375. e.RoutedEvent = LowerThumbDragStartedEvent;
  1376. RaiseEvent(e);
  1377. }
  1378. private void LeftThumbDragDelta(object sender, DragDeltaEventArgs e)
  1379. {
  1380. double change = Orientation == Orientation.Horizontal
  1381. ? e.HorizontalChange
  1382. : e.VerticalChange;
  1383. if (!IsSnapToTickEnabled)
  1384. {
  1385. MoveThumb(_leftButton, _centerThumb, change, Orientation, out _direction);
  1386. ReCalculateRangeSelected(true, false, _direction);
  1387. }
  1388. else
  1389. {
  1390. Direction localDirection;
  1391. Point currentPoint = Mouse.GetPosition(_container);
  1392. if (Orientation == Orientation.Horizontal)
  1393. {
  1394. if (currentPoint.X >= 0 &&
  1395. currentPoint.X <
  1396. _container.ActualWidth -
  1397. (_rightButton.ActualWidth + _rightThumb.ActualWidth + _centerThumb.MinWidth))
  1398. {
  1399. localDirection = currentPoint.X > _basePoint.X ? Direction.Increase : Direction.Decrease;
  1400. JumpToNextTick(localDirection, ButtonType.BottomLeft, change, LowerValue, false);
  1401. }
  1402. }
  1403. else
  1404. {
  1405. if (currentPoint.Y <= _container.ActualHeight && currentPoint.Y > _rightButton.ActualHeight + _rightThumb.ActualHeight + _centerThumb.MinHeight)
  1406. {
  1407. localDirection = currentPoint.Y < _basePoint.Y ? Direction.Increase : Direction.Decrease;
  1408. JumpToNextTick(localDirection, ButtonType.BottomLeft, -change, LowerValue, false);
  1409. }
  1410. }
  1411. }
  1412. _basePoint = Mouse.GetPosition(_container);
  1413. if (AutoToolTipPlacement != AutoToolTipPlacement.None)
  1414. {
  1415. _autoToolTip.Content = GetLowerToolTipNumber();
  1416. RelocateAutoToolTip();
  1417. }
  1418. e.RoutedEvent = LowerThumbDragDeltaEvent;
  1419. RaiseEvent(e);
  1420. }
  1421. private void LeftThumbDragComplete(object sender, DragCompletedEventArgs e)
  1422. {
  1423. if (_autoToolTip != null)
  1424. {
  1425. _autoToolTip.IsOpen = false;
  1426. _autoToolTip = null;
  1427. }
  1428. e.RoutedEvent = LowerThumbDragCompletedEvent;
  1429. RaiseEvent(e);
  1430. }
  1431. private void RightThumbDragStart(object sender, DragStartedEventArgs e)
  1432. {
  1433. _isMoved = true;
  1434. if (AutoToolTipPlacement != AutoToolTipPlacement.None)
  1435. {
  1436. if (_autoToolTip == null)
  1437. {
  1438. _autoToolTip = new ToolTip();
  1439. _autoToolTip.Placement = PlacementMode.Custom;
  1440. _autoToolTip.CustomPopupPlacementCallback = PopupPlacementCallback;
  1441. }
  1442. _autoToolTip.Content = GetUpperToolTipNumber();
  1443. _autoToolTip.PlacementTarget = _rightThumb;
  1444. _autoToolTip.IsOpen = true;
  1445. }
  1446. _basePoint = Mouse.GetPosition(_container);
  1447. e.RoutedEvent = UpperThumbDragStartedEvent;
  1448. RaiseEvent(e);
  1449. }
  1450. private void RightThumbDragDelta(object sender, DragDeltaEventArgs e)
  1451. {
  1452. double change = Orientation == Orientation.Horizontal
  1453. ? e.HorizontalChange
  1454. : e.VerticalChange;
  1455. if (!IsSnapToTickEnabled)
  1456. {
  1457. MoveThumb(_centerThumb, _rightButton, change, Orientation, out _direction);
  1458. ReCalculateRangeSelected(false, true, _direction);
  1459. }
  1460. else
  1461. {
  1462. Direction localDirection;
  1463. Point currentPoint = Mouse.GetPosition(_container);
  1464. if (Orientation == Orientation.Horizontal)
  1465. {
  1466. if (currentPoint.X < _container.ActualWidth &&
  1467. currentPoint.X > _leftButton.ActualWidth + _leftThumb.ActualWidth + _centerThumb.MinWidth)
  1468. {
  1469. localDirection = currentPoint.X > _basePoint.X ? Direction.Increase : Direction.Decrease;
  1470. JumpToNextTick(localDirection, ButtonType.TopRight, change, UpperValue, false);
  1471. }
  1472. }
  1473. else
  1474. {
  1475. if (currentPoint.Y >= 0 && currentPoint.Y < _container.ActualHeight -
  1476. (_leftButton.ActualHeight + _leftThumb.ActualHeight + _centerThumb.MinHeight))
  1477. {
  1478. localDirection = currentPoint.Y < _basePoint.Y ? Direction.Increase : Direction.Decrease;
  1479. JumpToNextTick(localDirection, ButtonType.TopRight, -change, UpperValue, false);
  1480. }
  1481. }
  1482. _basePoint = Mouse.GetPosition(_container);
  1483. }
  1484. if (AutoToolTipPlacement != AutoToolTipPlacement.None)
  1485. {
  1486. _autoToolTip.Content = GetUpperToolTipNumber();
  1487. RelocateAutoToolTip();
  1488. }
  1489. e.RoutedEvent = UpperThumbDragDeltaEvent;
  1490. RaiseEvent(e);
  1491. }
  1492. private void RightThumbDragComplete(object sender, DragCompletedEventArgs e)
  1493. {
  1494. if (_autoToolTip != null)
  1495. {
  1496. _autoToolTip.IsOpen = false;
  1497. _autoToolTip = null;
  1498. }
  1499. e.RoutedEvent = UpperThumbDragCompletedEvent;
  1500. RaiseEvent(e);
  1501. }
  1502. private void CenterThumbDragStarted(object sender, DragStartedEventArgs e)
  1503. {
  1504. _isMoved = true;
  1505. if (AutoToolTipPlacement != AutoToolTipPlacement.None)
  1506. {
  1507. if (_autoToolTip == null)
  1508. {
  1509. _autoToolTip = new ToolTip
  1510. {
  1511. Placement = PlacementMode.Custom,
  1512. CustomPopupPlacementCallback = PopupPlacementCallback
  1513. };
  1514. }
  1515. _autoToolTip.Content = GetLowerToolTipNumber() + " ; " + GetUpperToolTipNumber();
  1516. _autoToolTip.PlacementTarget = _centerThumb;
  1517. _autoToolTip.IsOpen = true;
  1518. }
  1519. _basePoint = Mouse.GetPosition(_container);
  1520. e.RoutedEvent = CentralThumbDragStartedEvent;
  1521. RaiseEvent(e);
  1522. }
  1523. private void CenterThumbDragDelta(object sender, DragDeltaEventArgs e)
  1524. {
  1525. if (!_centerThumbBlocked)
  1526. {
  1527. double change = Orientation == Orientation.Horizontal
  1528. ? e.HorizontalChange
  1529. : e.VerticalChange;
  1530. if (!IsSnapToTickEnabled)
  1531. {
  1532. MoveThumb(_leftButton, _rightButton, change, Orientation, out _direction);
  1533. ReCalculateRangeSelected(true, true, _direction);
  1534. }
  1535. else
  1536. {
  1537. Direction localDirection;
  1538. Point currentPoint = Mouse.GetPosition(_container);
  1539. if (Orientation == Orientation.Horizontal)
  1540. {
  1541. if (currentPoint.X >= 0 &&
  1542. currentPoint.X < _container.ActualWidth)
  1543. {
  1544. localDirection = currentPoint.X > _basePoint.X ? Direction.Increase : Direction.Decrease;
  1545. JumpToNextTick(localDirection, ButtonType.Both, change,
  1546. localDirection == Direction.Increase ? UpperValue : LowerValue, false);
  1547. }
  1548. }
  1549. else
  1550. {
  1551. if (currentPoint.Y >= 0 &&
  1552. currentPoint.Y < _container.ActualHeight)
  1553. {
  1554. localDirection = currentPoint.Y < _basePoint.Y ? Direction.Increase : Direction.Decrease;
  1555. JumpToNextTick(localDirection, ButtonType.Both, -change,
  1556. localDirection == Direction.Increase ? UpperValue : LowerValue, false);
  1557. }
  1558. }
  1559. }
  1560. _basePoint = Mouse.GetPosition(_container);
  1561. if (AutoToolTipPlacement != AutoToolTipPlacement.None)
  1562. {
  1563. _autoToolTip.Content = GetLowerToolTipNumber() + " ; " + GetUpperToolTipNumber();
  1564. RelocateAutoToolTip();
  1565. }
  1566. }
  1567. e.RoutedEvent = CentralThumbDragDeltaEvent;
  1568. RaiseEvent(e);
  1569. }
  1570. private void CenterThumbDragCompleted(object sender, DragCompletedEventArgs e)
  1571. {
  1572. if (_autoToolTip != null)
  1573. {
  1574. _autoToolTip.IsOpen = false;
  1575. _autoToolTip = null;
  1576. }
  1577. e.RoutedEvent = CentralThumbDragCompletedEvent;
  1578. RaiseEvent(e);
  1579. }
  1580. #endregion
  1581. #region Helper methods
  1582. private static double GetChangeKeepPositive(double width, double increment)
  1583. {
  1584. return Math.Max(width + increment, 0) - width;
  1585. }
  1586. //Method updates end point, which is needed to correctly compare current position on the thumb with
  1587. //current width of button
  1588. private double UpdateEndPoint(ButtonType type, Direction dir)
  1589. {
  1590. double d = 0;
  1591. //if we increase value
  1592. if (dir == Direction.Increase)
  1593. {
  1594. if (type == ButtonType.BottomLeft)
  1595. {
  1596. d = Orientation == Orientation.Horizontal
  1597. ? _leftButton.ActualWidth + _leftThumb.ActualWidth
  1598. : ActualHeight -(_leftButton.ActualHeight + _leftThumb.ActualHeight);
  1599. }
  1600. else if (type == ButtonType.TopRight)
  1601. {
  1602. d = Orientation == Orientation.Horizontal
  1603. ? ActualWidth - _rightButton.ActualWidth
  1604. : _rightButton.ActualHeight;
  1605. }
  1606. else if (type == ButtonType.Both)
  1607. {
  1608. if (!_isInsideRange)
  1609. {
  1610. d = Orientation == Orientation.Horizontal
  1611. ? ActualWidth - _rightButton.ActualWidth
  1612. : _rightButton.ActualHeight;
  1613. }
  1614. else
  1615. {
  1616. d = Orientation == Orientation.Horizontal
  1617. ? _leftButton.ActualWidth + _leftThumb.ActualWidth
  1618. : ActualHeight - (_leftButton.ActualHeight + _leftThumb.ActualHeight);
  1619. }
  1620. }
  1621. }
  1622. else if (dir == Direction.Decrease)
  1623. {
  1624. if (type == ButtonType.BottomLeft)
  1625. {
  1626. d = Orientation == Orientation.Horizontal
  1627. ? _leftButton.ActualWidth
  1628. : ActualHeight - _leftButton.ActualHeight;
  1629. }
  1630. if (type == ButtonType.TopRight)
  1631. {
  1632. d = Orientation == Orientation.Horizontal
  1633. ? ActualWidth - _rightButton.ActualWidth - _rightThumb.ActualWidth
  1634. : _rightButton.ActualHeight+ _rightThumb.ActualHeight;
  1635. }
  1636. else if (type == ButtonType.Both)
  1637. {
  1638. if (!_isInsideRange)
  1639. {
  1640. d = Orientation == Orientation.Horizontal
  1641. ? _leftButton.ActualWidth
  1642. : ActualHeight - _leftButton.ActualHeight;
  1643. }
  1644. else
  1645. {
  1646. d = Orientation == Orientation.Horizontal
  1647. ? ActualWidth - _rightButton.ActualWidth - _rightThumb.ActualWidth
  1648. : _rightButton.ActualHeight + _rightThumb.ActualHeight;
  1649. }
  1650. }
  1651. }
  1652. return d;
  1653. }
  1654. private Boolean GetResult(Double currentPoint, Double endPoint, Direction direction)
  1655. {
  1656. if (direction == Direction.Increase)
  1657. {
  1658. return Orientation == Orientation.Horizontal && currentPoint > endPoint ||
  1659. Orientation == Orientation.Vertical && currentPoint < endPoint;
  1660. }
  1661. return Orientation == Orientation.Horizontal && currentPoint < endPoint ||
  1662. Orientation == Orientation.Vertical && currentPoint > endPoint;
  1663. }
  1664. //This is timer event, which starts when IsMoveToPoint = false
  1665. //Supports IsSnapToTick option
  1666. void MoveToNextValue(object sender, EventArgs e)
  1667. {
  1668. //Get updated position of cursor
  1669. _position = Mouse.GetPosition(_visualElementsContainer);
  1670. _currentpoint = Orientation == Orientation.Horizontal ? _position.X : _position.Y;
  1671. double endpoint = UpdateEndPoint(_bType, _direction);
  1672. bool result = GetResult(_currentpoint, endpoint, _direction);
  1673. double widthChange;
  1674. if (!IsSnapToTickEnabled)
  1675. {
  1676. widthChange = SmallChange;
  1677. if (_tickCount > 5)
  1678. {
  1679. widthChange = LargeChange;
  1680. }
  1681. _roundToPrecision = true;
  1682. if (!widthChange.ToString(CultureInfo.InvariantCulture).ToLower().Contains("e") &&
  1683. widthChange.ToString(CultureInfo.InvariantCulture).Contains("."))
  1684. {
  1685. string[] array =
  1686. widthChange.ToString(CultureInfo.InvariantCulture).Split('.');
  1687. _precision = array[1].Length;
  1688. }
  1689. else
  1690. {
  1691. _precision = 0;
  1692. }
  1693. //Change value sign according to Horizontal or Vertical orientation
  1694. widthChange = Orientation == Orientation.Horizontal ? widthChange : -widthChange;
  1695. //Change value sign one more time according to Increase or Decrease direction
  1696. widthChange = _direction == Direction.Increase ? widthChange : -widthChange;
  1697. if (result)
  1698. {
  1699. switch (_bType)
  1700. {
  1701. case ButtonType.BottomLeft:
  1702. MoveThumb(_leftButton, _centerThumb, widthChange*_density, Orientation, out _direction);
  1703. ReCalculateRangeSelected(true, false, _direction);
  1704. break;
  1705. case ButtonType.TopRight:
  1706. MoveThumb(_centerThumb, _rightButton, widthChange*_density, Orientation, out _direction);
  1707. ReCalculateRangeSelected(false, true, _direction);
  1708. break;
  1709. case ButtonType.Both:
  1710. MoveThumb(_leftButton, _rightButton, widthChange*_density, Orientation, out _direction);
  1711. ReCalculateRangeSelected(true, true, _direction);
  1712. break;
  1713. }
  1714. }
  1715. }
  1716. else
  1717. {
  1718. //Get the difference between current and next value
  1719. widthChange = CalculateNextTick(_direction, _currenValue, 0, true);
  1720. Double value = widthChange;
  1721. //Change value sign according to Horizontal or Vertical orientation
  1722. widthChange = Orientation == Orientation.Horizontal ? widthChange : -widthChange;
  1723. if (_direction == Direction.Increase)
  1724. {
  1725. if (result)
  1726. {
  1727. switch (_bType)
  1728. {
  1729. case ButtonType.BottomLeft:
  1730. MoveThumb(_leftButton, _centerThumb, widthChange*_density, Orientation);
  1731. ReCalculateRangeSelected(true, false, LowerValue + value, _direction);
  1732. break;
  1733. case ButtonType.TopRight:
  1734. MoveThumb(_centerThumb, _rightButton, widthChange*_density, Orientation);
  1735. ReCalculateRangeSelected(false, true, UpperValue + value, _direction);
  1736. break;
  1737. case ButtonType.Both:
  1738. MoveThumb(_leftButton, _rightButton, widthChange*_density, Orientation);
  1739. ReCalculateRangeSelected(LowerValue + value, UpperValue + value,
  1740. _direction);
  1741. break;
  1742. }
  1743. }
  1744. }
  1745. else if (_direction == Direction.Decrease)
  1746. {
  1747. if (result)
  1748. {
  1749. switch (_bType)
  1750. {
  1751. case ButtonType.BottomLeft:
  1752. MoveThumb(_leftButton, _centerThumb, -widthChange*_density, Orientation);
  1753. ReCalculateRangeSelected(true, false, LowerValue - value, _direction);
  1754. break;
  1755. case ButtonType.TopRight:
  1756. MoveThumb(_centerThumb, _rightButton, -widthChange*_density, Orientation);
  1757. ReCalculateRangeSelected(false, true, UpperValue - value, _direction);
  1758. break;
  1759. case ButtonType.Both:
  1760. MoveThumb(_leftButton, _rightButton, -widthChange*_density, Orientation);
  1761. ReCalculateRangeSelected(LowerValue - value, UpperValue - value,
  1762. _direction);
  1763. break;
  1764. }
  1765. }
  1766. }
  1767. }
  1768. _tickCount++;
  1769. }
  1770. //Helper method to handle snapToTick scenario and decrease amount of code
  1771. private void SnapToTickHandle(ButtonType type, Direction mDirection, Double difference)
  1772. {
  1773. Double value = difference;
  1774. //change sign of "difference" variable because Horizontal and Vertical orientations has are different directions
  1775. difference = Orientation == Orientation.Horizontal ? difference : -difference;
  1776. if (mDirection == Direction.Increase)
  1777. {
  1778. switch (type)
  1779. {
  1780. case ButtonType.TopRight:
  1781. if (UpperValue < Maximum)
  1782. {
  1783. MoveThumb(_centerThumb, _rightButton, difference*_density, Orientation);
  1784. ReCalculateRangeSelected(false, true, UpperValue + value, mDirection);
  1785. }
  1786. break;
  1787. case ButtonType.BottomLeft:
  1788. if (LowerValue < UpperValue - MinRange)
  1789. {
  1790. MoveThumb(_leftButton, _centerThumb, difference*_density, Orientation);
  1791. ReCalculateRangeSelected(true, false, LowerValue + value, mDirection);
  1792. }
  1793. break;
  1794. case ButtonType.Both:
  1795. if (UpperValue < Maximum)
  1796. {
  1797. MoveThumb(_leftButton, _rightButton, difference*_density, Orientation);
  1798. ReCalculateRangeSelected(LowerValue + value, UpperValue + value, mDirection);
  1799. }
  1800. break;
  1801. }
  1802. }
  1803. else
  1804. {
  1805. switch (type)
  1806. {
  1807. case ButtonType.TopRight:
  1808. if (UpperValue > LowerValue + MinRange)
  1809. {
  1810. MoveThumb(_centerThumb, _rightButton, -difference*_density, Orientation);
  1811. ReCalculateRangeSelected(false, true, UpperValue - value, mDirection);
  1812. }
  1813. break;
  1814. case ButtonType.BottomLeft:
  1815. if (LowerValue > Minimum)
  1816. {
  1817. MoveThumb(_leftButton, _centerThumb, -difference*_density, Orientation);
  1818. ReCalculateRangeSelected(true, false, LowerValue - value, mDirection);
  1819. }
  1820. break;
  1821. case ButtonType.Both:
  1822. if (LowerValue > Minimum)
  1823. {
  1824. MoveThumb(_leftButton, _rightButton, -difference*_density, Orientation);
  1825. ReCalculateRangeSelected(LowerValue - value, UpperValue - value, mDirection);
  1826. }
  1827. break;
  1828. }
  1829. }
  1830. }
  1831. //Calculating next value for Tick
  1832. private Double CalculateNextTick(Direction dir, double chekingValue, double distance, bool moveDirectlyToNextTick)
  1833. {
  1834. if (!IsMoveToPointEnabled)
  1835. {
  1836. //Check if current value is exactly Tick value or it situated between Ticks
  1837. if (!IsDoubleCloseToInt((chekingValue - Minimum) / TickFrequency))
  1838. {
  1839. double x = (chekingValue - Minimum) / TickFrequency;
  1840. distance = TickFrequency * (int)x;
  1841. if (dir == Direction.Increase)
  1842. {
  1843. distance += TickFrequency;
  1844. }
  1845. distance = (distance - Math.Abs(chekingValue - Minimum));
  1846. _currenValue = 0;
  1847. return Math.Abs(distance);
  1848. }
  1849. }
  1850. //If we need move directly to next tick without calculating the difference between ticks
  1851. //Use when MoveToPoint disabled
  1852. if (moveDirectlyToNextTick)
  1853. {
  1854. distance = TickFrequency;
  1855. }
  1856. //If current value == tick (Value is divisible)
  1857. else
  1858. {
  1859. //current value in units (exactly in the place under cursor)
  1860. double currentValue = chekingValue - Minimum + (distance / _density);
  1861. double x = currentValue / TickFrequency;
  1862. if (dir == Direction.Increase)
  1863. {
  1864. double nextvalue = x.ToString(CultureInfo.InvariantCulture).ToLower().Contains("e+")
  1865. ? (x * TickFrequency) + TickFrequency
  1866. : ((int)x * TickFrequency) + TickFrequency;
  1867. distance = (nextvalue - Math.Abs(chekingValue - Minimum));
  1868. }
  1869. else
  1870. {
  1871. double previousValue = x.ToString(CultureInfo.InvariantCulture).ToLower().Contains("e+")
  1872. ? x * TickFrequency
  1873. : (int)x * TickFrequency;
  1874. distance = (Math.Abs(chekingValue - Minimum) - previousValue);
  1875. }
  1876. }
  1877. //return absolute value without sign not to depend on it if value is negative
  1878. //(could cause bugs in calcutaions if return not absolute value)
  1879. return Math.Abs(distance);
  1880. }
  1881. //Move thumb to next calculated Tick and update corresponding value
  1882. private void JumpToNextTick(Direction mDirection, ButtonType type, double distance, double chekingValue,
  1883. bool jumpDirectlyToTick)
  1884. {
  1885. //find the difference between current value and next value
  1886. double difference = CalculateNextTick(mDirection, chekingValue, distance, false);
  1887. Point p = Mouse.GetPosition(_visualElementsContainer);
  1888. double pos = Orientation == Orientation.Horizontal ? p.X : p.Y;
  1889. double widthHeight = Orientation == Orientation.Horizontal ? ActualWidth : ActualHeight;
  1890. double tickIntervalInPixels = mDirection == Direction.Increase
  1891. ? TickFrequency*_density
  1892. : -TickFrequency*_density;
  1893. if (jumpDirectlyToTick)
  1894. {
  1895. SnapToTickHandle(type, mDirection, difference);
  1896. }
  1897. else
  1898. {
  1899. if (mDirection == Direction.Increase)
  1900. {
  1901. if (!IsDoubleCloseToInt(chekingValue/TickFrequency))
  1902. {
  1903. if (distance > (difference*_density)/2 || (distance >= (widthHeight - pos) || distance >= pos))
  1904. {
  1905. SnapToTickHandle(type, mDirection, difference);
  1906. }
  1907. }
  1908. else
  1909. {
  1910. if ((distance > tickIntervalInPixels/2) || (distance >= (widthHeight - pos) || distance >= pos))
  1911. {
  1912. SnapToTickHandle(type, mDirection, difference);
  1913. }
  1914. }
  1915. }
  1916. else
  1917. {
  1918. if (!IsDoubleCloseToInt(chekingValue/TickFrequency))
  1919. {
  1920. if ((distance <= -(difference*_density)/2) || (UpperValue - LowerValue) < difference)
  1921. {
  1922. SnapToTickHandle(type, mDirection, difference);
  1923. }
  1924. }
  1925. else
  1926. {
  1927. if (distance < tickIntervalInPixels/2 || (UpperValue - LowerValue) < difference)
  1928. {
  1929. SnapToTickHandle(type, mDirection, difference);
  1930. }
  1931. }
  1932. }
  1933. }
  1934. }
  1935. //Change AutotoolTipPosition to move sync with Thumb
  1936. private void RelocateAutoToolTip()
  1937. {
  1938. var offset = _autoToolTip.HorizontalOffset;
  1939. _autoToolTip.HorizontalOffset = offset + 0.001;
  1940. _autoToolTip.HorizontalOffset = offset;
  1941. }
  1942. private Boolean IsValidDouble(Double d)
  1943. {
  1944. if (!Double.IsNaN(d) && !Double.IsInfinity(d))
  1945. {
  1946. return true;
  1947. }
  1948. return false;
  1949. }
  1950. //CHeck if two doubles approximately equals
  1951. private bool ApproximatelyEquals(double value1, double value2)
  1952. {
  1953. return Math.Abs(value1 - value2) <= Epsilon;
  1954. }
  1955. private Boolean IsDoubleCloseToInt(double val)
  1956. {
  1957. return ApproximatelyEquals(Math.Abs(val - Math.Round(val)), 0);
  1958. }
  1959. //Get lower value for autotooltip
  1960. private String GetLowerToolTipNumber()
  1961. {
  1962. NumberFormatInfo format = (NumberFormatInfo) (NumberFormatInfo.CurrentInfo.Clone());
  1963. format.NumberDecimalDigits = AutoToolTipPrecision;
  1964. return LowerValue.ToString("N", format);
  1965. }
  1966. //Get upper value for autotooltip
  1967. private String GetUpperToolTipNumber()
  1968. {
  1969. NumberFormatInfo format = (NumberFormatInfo) (NumberFormatInfo.CurrentInfo.Clone());
  1970. format.NumberDecimalDigits = AutoToolTipPrecision;
  1971. return UpperValue.ToString("N", format);
  1972. }
  1973. //CustomPopupPlacement callback for placing autotooltip int TopLeft or BottomRight position
  1974. private CustomPopupPlacement[] PopupPlacementCallback(Size popupSize, Size targetSize, Point offset)
  1975. {
  1976. switch (AutoToolTipPlacement)
  1977. {
  1978. case AutoToolTipPlacement.TopLeft:
  1979. if (Orientation == Orientation.Horizontal)
  1980. {
  1981. // Place popup at top of thumb
  1982. return new CustomPopupPlacement[]
  1983. {
  1984. new CustomPopupPlacement(
  1985. new Point((targetSize.Width - popupSize.Width)*0.5, -popupSize.Height),
  1986. PopupPrimaryAxis.Horizontal)
  1987. };
  1988. }
  1989. // Place popup at left of thumb
  1990. return new CustomPopupPlacement[]
  1991. {
  1992. new CustomPopupPlacement(
  1993. new Point(-popupSize.Width, (targetSize.Height - popupSize.Height)*0.5),
  1994. PopupPrimaryAxis.Vertical)
  1995. };
  1996. case AutoToolTipPlacement.BottomRight:
  1997. if (Orientation == Orientation.Horizontal)
  1998. {
  1999. // Place popup at bottom of thumb
  2000. return new CustomPopupPlacement[]
  2001. {
  2002. new CustomPopupPlacement(
  2003. new Point((targetSize.Width - popupSize.Width)*0.5, targetSize.Height),
  2004. PopupPrimaryAxis.Horizontal)
  2005. };
  2006. }
  2007. // Place popup at right of thumb
  2008. return new CustomPopupPlacement[]
  2009. {
  2010. new CustomPopupPlacement(
  2011. new Point(targetSize.Width, (targetSize.Height - popupSize.Height)*0.5),
  2012. PopupPrimaryAxis.Vertical)
  2013. };
  2014. default:
  2015. return new CustomPopupPlacement[] {};
  2016. }
  2017. }
  2018. #endregion
  2019. #region Validation methods
  2020. private static bool IsValidPrecision(object value)
  2021. {
  2022. return ((Int32) value >= 0);
  2023. }
  2024. private static bool IsValidMinRange(object value)
  2025. {
  2026. double d = (double)value;
  2027. if (d < 0.0 || Double.IsInfinity(d) || Double.IsNaN(d))
  2028. {
  2029. return false;
  2030. }
  2031. return true;
  2032. }
  2033. private static bool IsValidTickFrequency(object value)
  2034. {
  2035. double d = (double) value;
  2036. if (d <= 0.0 || Double.IsInfinity(d) || Double.IsNaN(d))
  2037. {
  2038. return false;
  2039. }
  2040. return true;
  2041. }
  2042. #endregion
  2043. #region Coerce callbacks
  2044. private static object CoerceMinimum(DependencyObject d, object basevalue)
  2045. {
  2046. RangeSlider rs = (RangeSlider)d;
  2047. double value = (double)basevalue;
  2048. if (value > rs.Maximum)
  2049. return rs.Maximum;
  2050. return basevalue;
  2051. }
  2052. private static object CoerceMaximum(DependencyObject d, object basevalue)
  2053. {
  2054. RangeSlider rs = (RangeSlider)d;
  2055. double value = (double)basevalue;
  2056. if (value < rs.Minimum)
  2057. return rs.Minimum;
  2058. return basevalue;
  2059. }
  2060. private static object CoerceLowerValue(DependencyObject d, object basevalue)
  2061. {
  2062. RangeSlider rs = (RangeSlider)d;
  2063. double value = (double)basevalue;
  2064. if (value < rs.Minimum || rs.UpperValue - rs.MinRange < rs.Minimum)
  2065. return rs.Minimum;
  2066. if (value > rs.UpperValue - rs.MinRange)
  2067. {
  2068. return rs.UpperValue - rs.MinRange;
  2069. }
  2070. return basevalue;
  2071. }
  2072. private static object CoerceUpperValue(DependencyObject d, object basevalue)
  2073. {
  2074. RangeSlider rs = (RangeSlider)d;
  2075. double value = (double)basevalue;
  2076. if (value > rs.Maximum || rs.LowerValue + rs.MinRange > rs.Maximum)
  2077. return rs.Maximum;
  2078. if (value < rs.LowerValue + rs.MinRange)
  2079. {
  2080. return rs.LowerValue + rs.MinRange;
  2081. }
  2082. return basevalue;
  2083. }
  2084. private static object CoerceMinRange(DependencyObject d, object basevalue)
  2085. {
  2086. RangeSlider rs = (RangeSlider)d;
  2087. double value = (double)basevalue;
  2088. if (rs.LowerValue + value > rs.Maximum)
  2089. {
  2090. return rs.Maximum - rs.LowerValue;
  2091. }
  2092. return basevalue;
  2093. }
  2094. private static object CoerceMinRangeWidth(DependencyObject d, object basevalue)
  2095. {
  2096. RangeSlider rs = (RangeSlider) d;
  2097. if (rs._leftThumb != null && rs._rightThumb != null)
  2098. {
  2099. double width;
  2100. if (rs.Orientation == Orientation.Horizontal)
  2101. {
  2102. width = rs.ActualWidth - rs._leftThumb.ActualWidth - rs._rightThumb.ActualWidth;
  2103. }
  2104. else
  2105. {
  2106. width = rs.ActualHeight - rs._leftThumb.ActualHeight - rs._rightThumb.ActualHeight;
  2107. }
  2108. return (Double)basevalue > width / 2 ? width / 2 : (Double)basevalue;
  2109. }
  2110. return basevalue;
  2111. }
  2112. #endregion
  2113. #region PropertyChanged CallBacks
  2114. private static void MaxPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
  2115. {
  2116. dependencyObject.CoerceValue(MaximumProperty);
  2117. dependencyObject.CoerceValue(MinimumProperty);
  2118. dependencyObject.CoerceValue(UpperValueProperty);
  2119. }
  2120. private static void MinPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
  2121. {
  2122. dependencyObject.CoerceValue(MinimumProperty);
  2123. dependencyObject.CoerceValue(MaximumProperty);
  2124. dependencyObject.CoerceValue(LowerValueProperty);
  2125. }
  2126. //Lower/Upper values property changed callback
  2127. private static void RangesChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
  2128. {
  2129. var slider = (RangeSlider)dependencyObject;
  2130. if (slider._internalUpdate)
  2131. return;
  2132. dependencyObject.CoerceValue(UpperValueProperty);
  2133. dependencyObject.CoerceValue(LowerValueProperty);
  2134. RaiseValueChangedEvents(dependencyObject);
  2135. slider._oldLower = slider.LowerValue;
  2136. slider._oldUpper = slider.UpperValue;
  2137. slider.ReCalculateSize();
  2138. }
  2139. private static void MinRangeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
  2140. {
  2141. Double value = (Double)e.NewValue;
  2142. if (value < 0)
  2143. value = 0;
  2144. var slider = (RangeSlider)dependencyObject;
  2145. dependencyObject.CoerceValue(MinRangeProperty);
  2146. slider._internalUpdate = true;
  2147. slider.UpperValue = Math.Max(slider.UpperValue, slider.LowerValue + value);
  2148. slider.UpperValue = Math.Min(slider.UpperValue, slider.Maximum);
  2149. slider._internalUpdate = false;
  2150. slider.CoerceValue(UpperValueProperty);
  2151. RaiseValueChangedEvents(dependencyObject);
  2152. slider._oldLower = slider.LowerValue;
  2153. slider._oldUpper = slider.UpperValue;
  2154. slider.ReCalculateSize();
  2155. }
  2156. private static void MinRangeWidthChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  2157. {
  2158. var slider = (RangeSlider)sender;
  2159. slider.ReCalculateSize();
  2160. }
  2161. private static void IntervalChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
  2162. {
  2163. RangeSlider rs = (RangeSlider)dependencyObject;
  2164. rs._timer.Interval = TimeSpan.FromMilliseconds((Int32)e.NewValue);
  2165. }
  2166. //Raises all value changes events
  2167. private static void RaiseValueChangedEvents(DependencyObject dependencyObject)
  2168. {
  2169. var slider = (RangeSlider) dependencyObject;
  2170. if (!Equals(slider._oldLower, slider.LowerValue) || !Equals(slider._oldUpper, slider.UpperValue))
  2171. {
  2172. slider.OnRangeSelectionChanged(new RangeSelectionChangedEventArgs(slider.LowerValue, slider.UpperValue,
  2173. slider._oldLower, slider._oldUpper));
  2174. }
  2175. if (!Equals(slider._oldLower, slider.LowerValue))
  2176. {
  2177. slider.OnRangeParameterChanged(
  2178. new RangeParameterChangedEventArgs(RangeParameterChangeType.Lower, slider._oldLower, slider.LowerValue),
  2179. LowerValueChangedEvent);
  2180. }
  2181. if (!Equals(slider._oldUpper, slider.UpperValue))
  2182. {
  2183. slider.OnRangeParameterChanged(
  2184. new RangeParameterChangedEventArgs(RangeParameterChangeType.Upper, slider._oldUpper, slider.UpperValue),
  2185. UpperValueChangedEvent);
  2186. }
  2187. }
  2188. #endregion
  2189. //enum for understanding which repeat button (left, right or both) is changing its width or height
  2190. enum ButtonType
  2191. {
  2192. BottomLeft,
  2193. TopRight,
  2194. Both
  2195. }
  2196. //enum for understanding current thumb moving direction
  2197. enum Direction
  2198. {
  2199. Increase,
  2200. Decrease
  2201. }
  2202. }
  2203. }