diff --git a/components/RangeSelector/samples/RangeSelector.md b/components/RangeSelector/samples/RangeSelector.md index 6b68bad4..530c0c0d 100644 --- a/components/RangeSelector/samples/RangeSelector.md +++ b/components/RangeSelector/samples/RangeSelector.md @@ -16,6 +16,12 @@ A `RangeSelector` is pretty similar to a regular `Slider`, and shares some of it > [!Sample RangeSelectorSample] +## Vertical Orientation + +The `RangeSelector` also supports vertical orientation. Set the `Orientation` property to `Vertical` to display the range selector vertically. + +> [!Sample RangeSelectorVerticalSample] + > [!NOTE] > If you are using a RangeSelector within a ScrollViewer you'll need to add some codes. This is because by default, the ScrollViewer will block the thumbs of the RangeSelector to capture the pointer. diff --git a/components/RangeSelector/samples/RangeSelectorVerticalSample.xaml b/components/RangeSelector/samples/RangeSelectorVerticalSample.xaml new file mode 100644 index 00000000..410e2524 --- /dev/null +++ b/components/RangeSelector/samples/RangeSelectorVerticalSample.xaml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/components/RangeSelector/samples/RangeSelectorVerticalSample.xaml.cs b/components/RangeSelector/samples/RangeSelectorVerticalSample.xaml.cs new file mode 100644 index 00000000..69fc7b7d --- /dev/null +++ b/components/RangeSelector/samples/RangeSelectorVerticalSample.xaml.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.WinUI.Controls; + +namespace RangeSelectorExperiment.Samples; + +[ToolkitSampleNumericOption("Minimum", 0, 0, 100, 1, false, Title = "Minimum")] +[ToolkitSampleNumericOption("Maximum", 100, 0, 100, 1, false, Title = "Maximum")] +[ToolkitSampleNumericOption("StepFrequency", 1, 0, 10, 1, false, Title = "StepFrequency")] +[ToolkitSampleBoolOption("Enable", true, Title = "IsEnabled")] + +[ToolkitSample(id: nameof(RangeSelectorVerticalSample), "Vertical RangeSelector", description: $"A sample for showing how to create and use a vertical {nameof(RangeSelector)} control.")] +public sealed partial class RangeSelectorVerticalSample : Page +{ + public RangeSelectorVerticalSample() + { + this.InitializeComponent(); + } +} diff --git a/components/RangeSelector/src/RangeSelector.Input.Drag.cs b/components/RangeSelector/src/RangeSelector.Input.Drag.cs index e8a8fd6a..ab84e06b 100644 --- a/components/RangeSelector/src/RangeSelector.Input.Drag.cs +++ b/components/RangeSelector/src/RangeSelector.Input.Drag.cs @@ -11,9 +11,12 @@ public partial class RangeSelector : Control { private void MinThumb_DragDelta(object sender, DragDeltaEventArgs e) { - _absolutePosition += e.HorizontalChange; + var isHorizontal = Orientation == Orientation.Horizontal; + _absolutePosition += isHorizontal ? e.HorizontalChange : e.VerticalChange; - RangeStart = DragThumb(_minThumb, 0, DragWidth(), _absolutePosition); + RangeStart = isHorizontal + ? DragThumb(_minThumb, 0, Canvas.GetLeft(_maxThumb), _absolutePosition) + : DragThumbVertical(_minThumb, Canvas.GetTop(_maxThumb), DragWidth(), _absolutePosition); if (_toolTipText != null) { @@ -23,9 +26,12 @@ private void MinThumb_DragDelta(object sender, DragDeltaEventArgs e) private void MaxThumb_DragDelta(object sender, DragDeltaEventArgs e) { - _absolutePosition += e.HorizontalChange; + var isHorizontal = Orientation == Orientation.Horizontal; + _absolutePosition += isHorizontal ? e.HorizontalChange : e.VerticalChange; - RangeEnd = DragThumb(_maxThumb, 0, DragWidth(), _absolutePosition); + RangeEnd = isHorizontal + ? DragThumb(_maxThumb, Canvas.GetLeft(_minThumb), DragWidth(), _absolutePosition) + : DragThumbVertical(_maxThumb, 0, Canvas.GetTop(_minThumb), _absolutePosition); if (_toolTipText != null) { @@ -67,7 +73,9 @@ private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e) private double DragWidth() { - return _containerCanvas!.ActualWidth - _maxThumb!.Width; + return Orientation == Orientation.Horizontal + ? _containerCanvas!.ActualWidth - _maxThumb!.Width + : _containerCanvas!.ActualHeight - _maxThumb!.Height; } private double DragThumb(Thumb? thumb, double min, double max, double nextPos) @@ -89,26 +97,68 @@ private double DragThumb(Thumb? thumb, double min, double max, double nextPos) return Minimum + ((nextPos / DragWidth()) * (Maximum - Minimum)); } + private double DragThumbVertical(Thumb? thumb, double min, double max, double nextPos) + { + nextPos = Math.Max(min, nextPos); + nextPos = Math.Min(max, nextPos); + + Canvas.SetTop(thumb, nextPos); + + if (_toolTip != null && thumb != null) + { + var thumbCenter = nextPos + (thumb.Height / 2); + _toolTip.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + var ttHeight = _toolTip.DesiredSize.Height / 2; + + Canvas.SetTop(_toolTip, thumbCenter - ttHeight); + UpdateToolTipPositionForVertical(); + } + + // Invert: top position (0) = Maximum, bottom position (DragWidth) = Minimum + return Maximum - ((nextPos / DragWidth()) * (Maximum - Minimum)); + } + private void Thumb_DragStarted(Thumb thumb) { var useMin = thumb == _minThumb; var otherThumb = useMin ? _maxThumb : _minThumb; + var isHorizontal = Orientation == Orientation.Horizontal; - _absolutePosition = Canvas.GetLeft(thumb); + _absolutePosition = isHorizontal ? Canvas.GetLeft(thumb) : Canvas.GetTop(thumb); Canvas.SetZIndex(thumb, 10); Canvas.SetZIndex(otherThumb, 0); _oldValue = RangeStart; if (_toolTip != null) { - _toolTip.Visibility = Visibility.Visible; - var thumbCenter = _absolutePosition + (thumb.Width / 2); - _toolTip.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - var ttWidth = _toolTip.ActualWidth / 2; - Canvas.SetLeft(_toolTip, thumbCenter - ttWidth); - - if (_toolTipText != null) - UpdateToolTipText(this, _toolTipText, useMin ? RangeStart : RangeEnd); + if (!isHorizontal && VerticalToolTipPlacement == VerticalToolTipPlacement.None) + { + _toolTip.Visibility = Visibility.Collapsed; + } + else + { + _toolTip.Visibility = Visibility.Visible; + + // Update tooltip text first so Measure gets accurate size + if (_toolTipText != null) + { + UpdateToolTipText(this, _toolTipText, useMin ? RangeStart : RangeEnd); + } + + _toolTip.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + if (isHorizontal) + { + var thumbCenter = _absolutePosition + (thumb.Width / 2); + Canvas.SetLeft(_toolTip, thumbCenter - (_toolTip.DesiredSize.Width / 2)); + } + else + { + var thumbCenter = _absolutePosition + (thumb.Height / 2); + Canvas.SetTop(_toolTip, thumbCenter - (_toolTip.DesiredSize.Height / 2)); + UpdateToolTipPositionForVertical(); + } + } } VisualStateManager.GoToState(this, useMin ? MinPressedState : MaxPressedState, true); diff --git a/components/RangeSelector/src/RangeSelector.Input.Key.cs b/components/RangeSelector/src/RangeSelector.Input.Key.cs index 24151c14..eeb4c888 100644 --- a/components/RangeSelector/src/RangeSelector.Input.Key.cs +++ b/components/RangeSelector/src/RangeSelector.Input.Key.cs @@ -20,56 +20,66 @@ public partial class RangeSelector : Control private void MinThumb_KeyDown(object sender, KeyRoutedEventArgs e) { + var isHorizontal = Orientation == Orientation.Horizontal; + var handled = false; + switch (e.Key) { - case VirtualKey.Left: + case VirtualKey.Left when isHorizontal: + case VirtualKey.Down when !isHorizontal: RangeStart -= StepFrequency; - SyncThumbs(fromMinKeyDown: true); - if (_toolTip != null) - { - _toolTip.Visibility = Visibility.Visible; - } - - e.Handled = true; + handled = true; break; - case VirtualKey.Right: + case VirtualKey.Right when isHorizontal: + case VirtualKey.Up when !isHorizontal: RangeStart += StepFrequency; - SyncThumbs(fromMinKeyDown: true); - if (_toolTip != null) - { - _toolTip.Visibility = Visibility.Visible; - } - - e.Handled = true; + handled = true; break; } + + if (handled) + { + SyncThumbs(fromMinKeyDown: true); + ShowToolTip(); + e.Handled = true; + } } private void MaxThumb_KeyDown(object sender, KeyRoutedEventArgs e) { + var isHorizontal = Orientation == Orientation.Horizontal; + var handled = false; + switch (e.Key) { - case VirtualKey.Left: + case VirtualKey.Left when isHorizontal: + case VirtualKey.Down when !isHorizontal: RangeEnd -= StepFrequency; - SyncThumbs(fromMaxKeyDown: true); - if (_toolTip != null) - { - _toolTip.Visibility = Visibility.Visible; - } - - e.Handled = true; + handled = true; break; - case VirtualKey.Right: + case VirtualKey.Right when isHorizontal: + case VirtualKey.Up when !isHorizontal: RangeEnd += StepFrequency; - SyncThumbs(fromMaxKeyDown: true); - if (_toolTip != null) - { - _toolTip.Visibility = Visibility.Visible; - } - - e.Handled = true; + handled = true; break; } + + if (handled) + { + SyncThumbs(fromMaxKeyDown: true); + ShowToolTip(); + e.Handled = true; + } + } + + private void ShowToolTip() + { + var isHorizontal = Orientation == Orientation.Horizontal; + if (!isHorizontal && VerticalToolTipPlacement == VerticalToolTipPlacement.None) return; + if (_toolTip != null) + { + _toolTip.Visibility = Visibility.Visible; + } } private void Thumb_KeyUp(object sender, KeyRoutedEventArgs e) @@ -78,6 +88,8 @@ private void Thumb_KeyUp(object sender, KeyRoutedEventArgs e) { case VirtualKey.Left: case VirtualKey.Right: + case VirtualKey.Up: + case VirtualKey.Down: if (_toolTip != null) { keyDebounceTimer.Debounce( diff --git a/components/RangeSelector/src/RangeSelector.Input.Pointer.cs b/components/RangeSelector/src/RangeSelector.Input.Pointer.cs index d27fd1be..fbd2023f 100644 --- a/components/RangeSelector/src/RangeSelector.Input.Pointer.cs +++ b/components/RangeSelector/src/RangeSelector.Input.Pointer.cs @@ -16,8 +16,12 @@ private void ContainerCanvas_PointerEntered(object sender, PointerRoutedEventArg private void ContainerCanvas_PointerExited(object sender, PointerRoutedEventArgs e) { - var position = e.GetCurrentPoint(_containerCanvas).Position.X; - var normalizedPosition = ((position / DragWidth()) * (Maximum - Minimum)) + Minimum; + var isHorizontal = Orientation == Orientation.Horizontal; + var point = e.GetCurrentPoint(_containerCanvas).Position; + var position = isHorizontal ? point.X : point.Y; + var normalizedPosition = isHorizontal + ? ((position / DragWidth()) * (Maximum - Minimum)) + Minimum + : Maximum - ((position / DragWidth()) * (Maximum - Minimum)); if (_pointerManipulatingMin) { @@ -40,13 +44,17 @@ private void ContainerCanvas_PointerExited(object sender, PointerRoutedEventArgs _toolTip.Visibility = Visibility.Collapsed; } - VisualStateManager.GoToState(this, "Normal", false); + VisualStateManager.GoToState(this, NormalState, false); } private void ContainerCanvas_PointerReleased(object sender, PointerRoutedEventArgs e) { - var position = e.GetCurrentPoint(_containerCanvas).Position.X; - var normalizedPosition = ((position / DragWidth()) * (Maximum - Minimum)) + Minimum; + var isHorizontal = Orientation == Orientation.Horizontal; + var point = e.GetCurrentPoint(_containerCanvas).Position; + var position = isHorizontal ? point.X : point.Y; + var normalizedPosition = isHorizontal + ? ((position / DragWidth()) * (Maximum - Minimum)) + Minimum + : Maximum - ((position / DragWidth()) * (Maximum - Minimum)); if (_pointerManipulatingMin) { @@ -74,12 +82,16 @@ private void ContainerCanvas_PointerReleased(object sender, PointerRoutedEventAr private void ContainerCanvas_PointerMoved(object sender, PointerRoutedEventArgs e) { - var position = e.GetCurrentPoint(_containerCanvas).Position.X; - var normalizedPosition = ((position / DragWidth()) * (Maximum - Minimum)) + Minimum; + var isHorizontal = Orientation == Orientation.Horizontal; + var point = e.GetCurrentPoint(_containerCanvas).Position; + var position = isHorizontal ? point.X : point.Y; if (_pointerManipulatingMin) { - RangeStart = DragThumb(_minThumb, 0, DragWidth(), position); + RangeStart = isHorizontal + ? DragThumb(_minThumb, 0, Canvas.GetLeft(_maxThumb), position) + : DragThumbVertical(_minThumb, Canvas.GetTop(_maxThumb), DragWidth(), position); + if (_toolTipText is not null) { UpdateToolTipText(this, _toolTipText, RangeStart); @@ -87,9 +99,12 @@ private void ContainerCanvas_PointerMoved(object sender, PointerRoutedEventArgs } else if (_pointerManipulatingMax) { + RangeEnd = isHorizontal + ? DragThumb(_maxThumb, Canvas.GetLeft(_minThumb), DragWidth(), position) + : DragThumbVertical(_maxThumb, 0, Canvas.GetTop(_minThumb), position); + if (_toolTipText is not null) { - RangeEnd = DragThumb(_maxThumb, 0, DragWidth(), position); UpdateToolTipText(this, _toolTipText, RangeEnd); } } @@ -97,8 +112,13 @@ private void ContainerCanvas_PointerMoved(object sender, PointerRoutedEventArgs private void ContainerCanvas_PointerPressed(object sender, PointerRoutedEventArgs e) { - var position = e.GetCurrentPoint(_containerCanvas).Position.X; - var normalizedPosition = position * Math.Abs(Maximum - Minimum) / DragWidth(); + var isHorizontal = Orientation == Orientation.Horizontal; + var point = e.GetCurrentPoint(_containerCanvas).Position; + var position = isHorizontal ? point.X : point.Y; + var normalizedPosition = isHorizontal + ? position * Math.Abs(Maximum - Minimum) / DragWidth() + : Maximum - ((position / DragWidth()) * (Maximum - Minimum)); + double upperValueDiff = Math.Abs(RangeEnd - normalizedPosition); double lowerValueDiff = Math.Abs(RangeStart - normalizedPosition); diff --git a/components/RangeSelector/src/RangeSelector.Properties.cs b/components/RangeSelector/src/RangeSelector.Properties.cs index 6377df37..9d75e075 100644 --- a/components/RangeSelector/src/RangeSelector.Properties.cs +++ b/components/RangeSelector/src/RangeSelector.Properties.cs @@ -9,6 +9,16 @@ namespace CommunityToolkit.WinUI.Controls; /// public partial class RangeSelector : Control { + /// + /// Identifies the property. + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register( + nameof(Orientation), + typeof(Orientation), + typeof(RangeSelector), + new PropertyMetadata(Orientation.Horizontal, OrientationChangedCallback)); + /// /// Identifies the property. /// @@ -59,6 +69,16 @@ public partial class RangeSelector : Control typeof(RangeSelector), new PropertyMetadata(DefaultStepFrequency)); + /// + /// Identifies the property. + /// + public static readonly DependencyProperty VerticalToolTipPlacementProperty = + DependencyProperty.Register( + nameof(VerticalToolTipPlacement), + typeof(VerticalToolTipPlacement), + typeof(RangeSelector), + new PropertyMetadata(VerticalToolTipPlacement.Right)); + /// /// Gets or sets the absolute minimum value of the range. /// @@ -119,6 +139,45 @@ public double StepFrequency set => SetValue(StepFrequencyProperty, value); } + /// + /// Gets or sets the orientation of the range selector (horizontal or vertical). + /// + /// + /// The orientation of the range selector. Default is . + /// + public Orientation Orientation + { + get => (Orientation)GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + + /// + /// Gets or sets the placement of the tooltip for the vertical range selector. + /// This property only takes effect when is set to . + /// + /// + /// The placement of the tooltip. Default is . + /// + public VerticalToolTipPlacement VerticalToolTipPlacement + { + get => (VerticalToolTipPlacement)GetValue(VerticalToolTipPlacementProperty); + set => SetValue(VerticalToolTipPlacementProperty, value); + } + + private static void OrientationChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var rangeSelector = d as RangeSelector; + + if (rangeSelector == null || !rangeSelector._valuesAssigned) + { + return; + } + + VisualStateManager.GoToState(rangeSelector, rangeSelector.Orientation == Orientation.Horizontal ? HorizontalState : VerticalState, true); + + rangeSelector.SyncThumbs(); + } + private static void MinimumChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { var rangeSelector = d as RangeSelector; diff --git a/components/RangeSelector/src/RangeSelector.ToolTip.Placement.cs b/components/RangeSelector/src/RangeSelector.ToolTip.Placement.cs new file mode 100644 index 00000000..3ff8ee28 --- /dev/null +++ b/components/RangeSelector/src/RangeSelector.ToolTip.Placement.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace CommunityToolkit.WinUI.Controls; + +/// +/// Enumeration used to determine the placement of the tooltip +/// for the vertical RangeSelector. +/// +public enum VerticalToolTipPlacement +{ + /// + /// Tooltip is placed to the right of the thumb. + /// + Right, + + /// + /// Tooltip is placed to the left of the thumb. + /// + Left, + + /// + /// Tooltip is not displayed. + /// + None +} diff --git a/components/RangeSelector/src/RangeSelector.cs b/components/RangeSelector/src/RangeSelector.cs index 7cf3bdbb..55805d6d 100644 --- a/components/RangeSelector/src/RangeSelector.cs +++ b/components/RangeSelector/src/RangeSelector.cs @@ -16,6 +16,8 @@ namespace CommunityToolkit.WinUI.Controls; [TemplateVisualState(Name = MinPressedState, GroupName = CommonStates)] [TemplateVisualState(Name = MaxPressedState, GroupName = CommonStates)] [TemplateVisualState(Name = DisabledState, GroupName = CommonStates)] +[TemplateVisualState(Name = HorizontalState, GroupName = OrientationStates)] +[TemplateVisualState(Name = VerticalState, GroupName = OrientationStates)] [TemplatePart(Name = "OutOfRangeContentContainer", Type = typeof(Border))] [TemplatePart(Name = "ActiveRectangle", Type = typeof(Rectangle))] [TemplatePart(Name = "MinThumb", Type = typeof(Thumb))] @@ -33,6 +35,9 @@ public partial class RangeSelector : Control internal const string DisabledState = "Disabled"; internal const string MinPressedState = "MinPressed"; internal const string MaxPressedState = "MaxPressed"; + internal const string OrientationStates = "OrientationStates"; + internal const string HorizontalState = "Horizontal"; + internal const string VerticalState = "Vertical"; private const double Epsilon = 0.01; private const double DefaultMinimum = 0.0; @@ -135,6 +140,7 @@ protected override void OnApplyTemplate() } VisualStateManager.GoToState(this, IsEnabled ? NormalState : DisabledState, false); + VisualStateManager.GoToState(this, Orientation == Orientation.Horizontal ? HorizontalState : VerticalState, false); IsEnabledChanged += RangeSelector_IsEnabledChanged; @@ -142,9 +148,19 @@ protected override void OnApplyTemplate() var tb = new TextBlock { Text = Maximum.ToString() }; tb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + // Ensure thumbs and active rectangle are synced after the control is fully loaded + Loaded -= RangeSelector_Loaded; + Loaded += RangeSelector_Loaded; + base.OnApplyTemplate(); } + private void RangeSelector_Loaded(object sender, RoutedEventArgs e) + { + Loaded -= RangeSelector_Loaded; + SyncThumbs(); + } + private static void UpdateToolTipText(RangeSelector rangeSelector, TextBlock toolTip, double newValue) { if (toolTip != null) @@ -237,24 +253,46 @@ private double MoveToStepFrequency(double rangeValue) private void SyncThumbs(bool fromMinKeyDown = false, bool fromMaxKeyDown = false) { - if (_containerCanvas == null) + if (_containerCanvas == null || _minThumb == null || _maxThumb == null) { return; } - var relativeLeft = ((RangeStart - Minimum) / (Maximum - Minimum)) * DragWidth(); - var relativeRight = ((RangeEnd - Minimum) / (Maximum - Minimum)) * DragWidth(); + var relativeStart = ((RangeStart - Minimum) / (Maximum - Minimum)) * DragWidth(); + var relativeEnd = ((RangeEnd - Minimum) / (Maximum - Minimum)) * DragWidth(); + var isHorizontal = Orientation == Orientation.Horizontal; - Canvas.SetLeft(_minThumb, relativeLeft); - Canvas.SetLeft(_maxThumb, relativeRight); + if (isHorizontal) + { + Canvas.SetLeft(_minThumb, relativeStart); + Canvas.SetLeft(_maxThumb, relativeEnd); + } + else + { + // Vertical: invert positions so min is at bottom, max is at top + Canvas.SetTop(_minThumb, DragWidth() - relativeStart); + Canvas.SetTop(_maxThumb, DragWidth() - relativeEnd); + } if (fromMinKeyDown || fromMaxKeyDown) { - DragThumb( - fromMinKeyDown ? _minThumb : _maxThumb, - fromMinKeyDown ? 0 : Canvas.GetLeft(_minThumb), - fromMinKeyDown ? Canvas.GetLeft(_maxThumb) : DragWidth(), - fromMinKeyDown ? relativeLeft : relativeRight); + var thumb = fromMinKeyDown ? _minThumb : _maxThumb; + var position = fromMinKeyDown ? relativeStart : relativeEnd; + + if (isHorizontal) + { + var min = fromMinKeyDown ? 0 : Canvas.GetLeft(_minThumb); + var max = fromMinKeyDown ? Canvas.GetLeft(_maxThumb) : DragWidth(); + DragThumb(thumb, min, max, position); + } + else + { + var invertedPosition = DragWidth() - position; + var min = fromMinKeyDown ? Canvas.GetTop(_maxThumb) : 0; + var max = fromMinKeyDown ? DragWidth() : Canvas.GetTop(_minThumb); + DragThumbVertical(thumb, min, max, invertedPosition); + } + if (_toolTipText != null) { UpdateToolTipText(this, _toolTipText, fromMinKeyDown ? RangeStart : RangeEnd); @@ -266,29 +304,55 @@ private void SyncThumbs(bool fromMinKeyDown = false, bool fromMaxKeyDown = false private void SyncActiveRectangle() { - if (_containerCanvas == null) + if (_containerCanvas == null || _minThumb == null || _maxThumb == null || _activeRectangle == null) { return; } - if (_minThumb == null) + if (Orientation == Orientation.Horizontal) { - return; + var relativeLeft = Canvas.GetLeft(_minThumb); + Canvas.SetLeft(_activeRectangle, relativeLeft); + Canvas.SetTop(_activeRectangle, (_containerCanvas.ActualHeight - _activeRectangle.ActualHeight) / 2); + _activeRectangle.Width = Math.Max(0, Canvas.GetLeft(_maxThumb) - relativeLeft); } - - if (_maxThumb == null) + else { - return; + var relativeTop = Canvas.GetTop(_maxThumb); + Canvas.SetTop(_activeRectangle, relativeTop); + Canvas.SetLeft(_activeRectangle, (_containerCanvas.ActualWidth - _activeRectangle.ActualWidth) / 2); + _activeRectangle.Height = Math.Max(0, Canvas.GetTop(_minThumb) - relativeTop); } - - var relativeLeft = Canvas.GetLeft(_minThumb); - Canvas.SetLeft(_activeRectangle, relativeLeft); - Canvas.SetTop(_activeRectangle, (_containerCanvas.ActualHeight - _activeRectangle!.ActualHeight) / 2); - _activeRectangle.Width = Math.Max(0, Canvas.GetLeft(_maxThumb) - Canvas.GetLeft(_minThumb)); } private void RangeSelector_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) { VisualStateManager.GoToState(this, IsEnabled ? NormalState : DisabledState, true); } + + private void UpdateToolTipPositionForVertical() + { + if (_toolTip == null || _containerCanvas == null) + { + return; + } + + // Offset to position tooltip beside the thumb + const double toolTipOffset = 52; + + switch (VerticalToolTipPlacement) + { + case VerticalToolTipPlacement.Right: + Canvas.SetLeft(_toolTip, toolTipOffset); + break; + case VerticalToolTipPlacement.Left: + _toolTip.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + var toolTipWidth = _toolTip.DesiredSize.Width; + Canvas.SetLeft(_toolTip, -toolTipWidth - (toolTipOffset - _containerCanvas.ActualWidth)); + break; + case VerticalToolTipPlacement.None: + _toolTip.Visibility = Visibility.Collapsed; + break; + } + } } diff --git a/components/RangeSelector/src/RangeSelector.xaml b/components/RangeSelector/src/RangeSelector.xaml index 934c62a3..456afb0f 100644 --- a/components/RangeSelector/src/RangeSelector.xaml +++ b/components/RangeSelector/src/RangeSelector.xaml @@ -17,7 +17,8 @@ - + + @@ -246,6 +247,25 @@ + + + + + + + + + + + + + + + + + + +