From c529ceae56ef57195e64f2a5c5f30b839ff672b8 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Sun, 2 Jun 2019 23:25:46 +0800 Subject: [PATCH] completed the branch --- .../HandyControlDemo_Core30.csproj | 3 + src/HandyControl.sln | 16 - .../HandyControlDemo_Net_40.csproj | 3 + .../Controls/RangeSliderDemoCtl.xaml | 17 +- .../Controls/Cover/CoverView/CoverView.cs | 2 +- .../Cover/CoverView/CoverViewContent.cs | 4 +- .../HandyControl_Shared/Controls/Grid/Row.cs | 14 +- .../Controls/Input/NumericUpDown.cs | 6 +- .../{ => CompareSlider}/CompareSlider.cs | 0 .../{ => CompareSlider}/CompareTrack.cs | 0 .../Controls/Slider/RangeSlider.cs | 15 - .../Slider/RangeSlider/RangeSlider.cs | 655 ++++++++++++++++++ .../Controls/Slider/RangeSlider/RangeThumb.cs | 46 ++ .../Controls/Slider/RangeSlider/RangeTrack.cs | 444 ++++++++++++ .../Slider/RangeSlider/TwoWayRangeBase.cs | 181 +++++ .../HandyControl_Shared/Data/ValueBoxes.cs | 4 + .../HandyControl_Shared.projitems | 13 +- .../Styles/Base/ColorPickerBaseStyle.xaml | 245 +++++++ .../Themes/Styles/Base/SliderBaseStyle.xaml | 285 ++++++++ .../Themes/Styles/ColorPicker.xaml | 122 +--- .../Themes/Styles/Slider.xaml | 123 +--- .../Tools/Helper/ArithmeticHelper.cs | 17 +- .../Tools/Helper/ValidateHelper.cs | 76 +- .../Tools/Helper/VisualHelper.cs | 2 +- 24 files changed, 1978 insertions(+), 315 deletions(-) rename src/Shared/HandyControl_Shared/Controls/Slider/{ => CompareSlider}/CompareSlider.cs (100%) rename src/Shared/HandyControl_Shared/Controls/Slider/{ => CompareSlider}/CompareTrack.cs (100%) delete mode 100644 src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider.cs create mode 100644 src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeSlider.cs create mode 100644 src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeThumb.cs create mode 100644 src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeTrack.cs create mode 100644 src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs create mode 100644 src/Shared/HandyControl_Shared/Themes/Styles/Base/ColorPickerBaseStyle.xaml diff --git a/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj b/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj index 0fc398af..796f9876 100644 --- a/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj +++ b/src/Core_30/HandyControlDemo_Core_30/HandyControlDemo_Core30.csproj @@ -349,6 +349,9 @@ Resources\Img\LeftMainContent\MainMenuControl_16x.png + + Resources\Img\LeftMainContent\brackets_Square_16xLG.png + diff --git a/src/HandyControl.sln b/src/HandyControl.sln index be1e539b..e6c455be 100644 --- a/src/HandyControl.sln +++ b/src/HandyControl.sln @@ -95,13 +95,9 @@ Global {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Core30|Any CPU.Build.0 = Debug|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net40|Any CPU.ActiveCfg = Debug|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net45|Any CPU.ActiveCfg = Debug|Any CPU - {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net45|Any CPU.Build.0 = Debug|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net462|Any CPU.ActiveCfg = Debug|Any CPU - {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net462|Any CPU.Build.0 = Debug|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net47|Any CPU.ActiveCfg = Debug|Any CPU - {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net47|Any CPU.Build.0 = Debug|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net48|Any CPU.ActiveCfg = Debug|Any CPU - {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Debug-Net48|Any CPU.Build.0 = Debug|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Release-Core30|Any CPU.ActiveCfg = Release|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Release-Core30|Any CPU.Build.0 = Release|Any CPU {BE4A567D-B6E0-413E-8F64-E1A53B4F9366}.Release-Net40|Any CPU.ActiveCfg = Release|Any CPU @@ -117,13 +113,9 @@ Global {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Core30|Any CPU.Build.0 = Debug|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net40|Any CPU.ActiveCfg = Debug|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net45|Any CPU.ActiveCfg = Debug|Any CPU - {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net45|Any CPU.Build.0 = Debug|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net462|Any CPU.ActiveCfg = Debug|Any CPU - {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net462|Any CPU.Build.0 = Debug|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net47|Any CPU.ActiveCfg = Debug|Any CPU - {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net47|Any CPU.Build.0 = Debug|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net48|Any CPU.ActiveCfg = Debug|Any CPU - {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Debug-Net48|Any CPU.Build.0 = Debug|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Release-Core30|Any CPU.ActiveCfg = Release|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Release-Core30|Any CPU.Build.0 = Release|Any CPU {D7107690-8592-4F9F-9379-9B82C1C15D2A}.Release-Net40|Any CPU.ActiveCfg = Release|Any CPU @@ -139,13 +131,9 @@ Global {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net40|Any CPU.ActiveCfg = Debug|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net40|Any CPU.Build.0 = Debug|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net45|Any CPU.ActiveCfg = Debug|Any CPU - {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net45|Any CPU.Build.0 = Debug|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net462|Any CPU.ActiveCfg = Debug|Any CPU - {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net462|Any CPU.Build.0 = Debug|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net47|Any CPU.ActiveCfg = Debug|Any CPU - {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net47|Any CPU.Build.0 = Debug|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net48|Any CPU.ActiveCfg = Debug|Any CPU - {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Debug-Net48|Any CPU.Build.0 = Debug|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Release-Core30|Any CPU.ActiveCfg = Release|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Release-Net40|Any CPU.ActiveCfg = Release|Any CPU {32204503-2EF0-4681-AE13-AA1FEB6C658A}.Release-Net40|Any CPU.Build.0 = Release|Any CPU @@ -161,13 +149,9 @@ Global {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net40|Any CPU.ActiveCfg = Debug|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net40|Any CPU.Build.0 = Debug|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net45|Any CPU.ActiveCfg = Debug|Any CPU - {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net45|Any CPU.Build.0 = Debug|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net462|Any CPU.ActiveCfg = Debug|Any CPU - {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net462|Any CPU.Build.0 = Debug|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net47|Any CPU.ActiveCfg = Debug|Any CPU - {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net47|Any CPU.Build.0 = Debug|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net48|Any CPU.ActiveCfg = Debug|Any CPU - {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Debug-Net48|Any CPU.Build.0 = Debug|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Release-Core30|Any CPU.ActiveCfg = Release|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Release-Net40|Any CPU.ActiveCfg = Release|Any CPU {D8AE88F8-C36B-4D10-A7F9-22FFCFBA5231}.Release-Net40|Any CPU.Build.0 = Release|Any CPU diff --git a/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj b/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj index f6f98c80..61536a9e 100644 --- a/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj +++ b/src/Net_40/HandyControlDemo_Net_40/HandyControlDemo_Net_40.csproj @@ -395,6 +395,9 @@ TextTemplatingFileGenerator MessageToken.cs + + Resources\Img\LeftMainContent\brackets_Square_16xLG.png + Resources\Img\LeftMainContent\GridPane_16x.png diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/RangeSliderDemoCtl.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/RangeSliderDemoCtl.xaml index 9a8c3727..0dd0f1b7 100644 --- a/src/Shared/HandyControlDemo_Shared/UserControl/Controls/RangeSliderDemoCtl.xaml +++ b/src/Shared/HandyControlDemo_Shared/UserControl/Controls/RangeSliderDemoCtl.xaml @@ -3,5 +3,20 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:hc="https://handyorg.github.io/handycontrol" Background="{DynamicResource RegionBrush}"> - + + + + + + + + + + + + + + + + diff --git a/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverView.cs b/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverView.cs index 0e80402c..96c8bf46 100644 --- a/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverView.cs +++ b/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverView.cs @@ -135,7 +135,7 @@ namespace HandyControl.Controls public static readonly DependencyProperty ItemContentHeightProperty = DependencyProperty.Register( "ItemContentHeight", typeof(double), typeof(CoverView), new PropertyMetadata(ValueBoxes.Double300Box), - value => ValidateHelper.IsInRangeOfPosDouble(value, true)); + ValidateHelper.IsInRangeOfPosDoubleIncludeZero); public double ItemContentHeight { diff --git a/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverViewContent.cs b/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverViewContent.cs index eb7f3e8c..9cb74208 100644 --- a/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverViewContent.cs +++ b/src/Shared/HandyControl_Shared/Controls/Cover/CoverView/CoverViewContent.cs @@ -43,7 +43,7 @@ namespace HandyControl.Controls } internal static readonly DependencyProperty ManualHeightProperty = DependencyProperty.Register( - "ManualHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double0Box), value => ValidateHelper.IsInRangeOfPosDouble(value, true)); + "ManualHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double0Box), ValidateHelper.IsInRangeOfPosDoubleIncludeZero); internal double ManualHeight { @@ -53,7 +53,7 @@ namespace HandyControl.Controls public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register( "ContentHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double300Box), - value => ValidateHelper.IsInRangeOfPosDouble(value, true)); + ValidateHelper.IsInRangeOfPosDoubleIncludeZero); public double ContentHeight { diff --git a/src/Shared/HandyControl_Shared/Controls/Grid/Row.cs b/src/Shared/HandyControl_Shared/Controls/Grid/Row.cs index 155ed71b..e659178d 100644 --- a/src/Shared/HandyControl_Shared/Controls/Grid/Row.cs +++ b/src/Shared/HandyControl_Shared/Controls/Grid/Row.cs @@ -14,11 +14,9 @@ namespace HandyControl.Controls private double _maxChildDesiredHeight; public static readonly DependencyProperty GutterProperty = DependencyProperty.Register( - "Gutter", typeof(double), typeof(Row), new PropertyMetadata(ValueBoxes.Double0Box, null, OnGutterCoerce), OnGutterValidate); + "Gutter", typeof(double), typeof(Row), new PropertyMetadata(ValueBoxes.Double0Box, null, OnGutterCoerce), ValidateHelper.IsInRangeOfPosDoubleIncludeZero); - private static object OnGutterCoerce(DependencyObject d, object basevalue) => OnGutterValidate(basevalue) ? basevalue : .0; - - private static bool OnGutterValidate(object value) => ValidateHelper.IsInRangeOfPosDouble(value, true); + private static object OnGutterCoerce(DependencyObject d, object basevalue) => ValidateHelper.IsInRangeOfPosDoubleIncludeZero(basevalue) ? basevalue : .0; public double Gutter { @@ -44,8 +42,6 @@ namespace HandyControl.Controls } } - var itemWidth = constraint.Width / ColLayout.ColMaxCellCount; - var childBounds = new Rect(-gutterHalf, -gutterHalf, 0, _maxChildDesiredHeight); _layoutStatus = ColLayout.GetLayoutStatus(constraint.Width); foreach (var child in InternalChildren.OfType()) @@ -55,15 +51,9 @@ namespace HandyControl.Controls if (totalCellCount > ColLayout.ColMaxCellCount) { - childBounds.X = -gutterHalf; - childBounds.Y += _maxChildDesiredHeight; totalCellCount = cellCount; totalRowCount++; } - - var childWidth = cellCount * itemWidth; - childBounds.Width = childWidth; - childBounds.X += childWidth + child.Offset * itemWidth; } return new Size(0, _maxChildDesiredHeight * totalRowCount - Gutter); diff --git a/src/Shared/HandyControl_Shared/Controls/Input/NumericUpDown.cs b/src/Shared/HandyControl_Shared/Controls/Input/NumericUpDown.cs index fa59e270..df9f0c73 100644 --- a/src/Shared/HandyControl_Shared/Controls/Input/NumericUpDown.cs +++ b/src/Shared/HandyControl_Shared/Controls/Input/NumericUpDown.cs @@ -153,7 +153,7 @@ namespace HandyControl.Controls public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(NumericUpDown), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - OnValueChanged, CoerceValue), ArithmeticHelper.IsValidDoubleValue); + OnValueChanged, CoerceValue), ValidateHelper.IsInRangeOfDouble); private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { @@ -208,7 +208,7 @@ namespace HandyControl.Controls /// 最大值 /// public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( - "Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum), ArithmeticHelper.IsValidDoubleValue); + "Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum), ValidateHelper.IsInRangeOfDouble); private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { @@ -236,7 +236,7 @@ namespace HandyControl.Controls /// 最小值 /// public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( - "Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.Double0Box, OnMinimumChanged, CoerceMinimum), ArithmeticHelper.IsValidDoubleValue); + "Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.Double0Box, OnMinimumChanged, CoerceMinimum), ValidateHelper.IsInRangeOfDouble); private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/CompareSlider.cs b/src/Shared/HandyControl_Shared/Controls/Slider/CompareSlider/CompareSlider.cs similarity index 100% rename from src/Shared/HandyControl_Shared/Controls/Slider/CompareSlider.cs rename to src/Shared/HandyControl_Shared/Controls/Slider/CompareSlider/CompareSlider.cs diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/CompareTrack.cs b/src/Shared/HandyControl_Shared/Controls/Slider/CompareSlider/CompareTrack.cs similarity index 100% rename from src/Shared/HandyControl_Shared/Controls/Slider/CompareTrack.cs rename to src/Shared/HandyControl_Shared/Controls/Slider/CompareSlider/CompareTrack.cs diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider.cs b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider.cs deleted file mode 100644 index 9b0b2b80..00000000 --- a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel; -using System.Windows; -using System.Windows.Controls.Primitives; - -namespace HandyControl.Controls -{ - [DefaultEvent("ValueChanged"), DefaultProperty("Value")] - [TemplatePart(Name = "PART_Track", Type = typeof(Track))] - [TemplatePart(Name = "PART_SelectionRange", Type = typeof(FrameworkElement))] - public class RangeSlider : RangeBase - { - - - } -} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeSlider.cs b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeSlider.cs new file mode 100644 index 00000000..19289546 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeSlider.cs @@ -0,0 +1,655 @@ +// Adapted from https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Slider.cs + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using HandyControl.Data; +using HandyControl.Expression.Drawing; +using HandyControl.Tools; + +namespace HandyControl.Controls +{ + [DefaultEvent("ValueChanged"), DefaultProperty("Value")] + [TemplatePart(Name = ElementTrack, Type = typeof(Track))] + public class RangeSlider : TwoWayRangeBase + { + private const string ElementTrack= "PART_Track"; + + private RangeTrack _track; + + private readonly ToolTip _autoToolTipStart = null; + + private readonly ToolTip _autoToolTipEnd = null; + + private RangeThumb _thumbCurrent; + + private object _thumbOriginalToolTip; + + private Point _originThumbPoint; + + private Point _previousScreenCoordPosition; + + static RangeSlider() + { + InitializeCommands(); + + MinimumProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsMeasure)); + MaximumProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(ValueBoxes.Double10Box, FrameworkPropertyMetadataOptions.AffectsMeasure)); + ValueStartProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsMeasure)); + ValueEndProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsMeasure)); + + // Register Event Handler for the Thumb + EventManager.RegisterClassHandler(typeof(RangeSlider), Thumb.DragStartedEvent, new DragStartedEventHandler(OnThumbDragStarted)); + EventManager.RegisterClassHandler(typeof(RangeSlider), Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnThumbDragDelta)); + EventManager.RegisterClassHandler(typeof(RangeSlider), Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnThumbDragCompleted)); + + // Listen to MouseLeftButtonDown event to determine if slide should move focus to itself + EventManager.RegisterClassHandler(typeof(RangeSlider), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), true); + } + + public RangeSlider() + { + CommandBindings.Add(new CommandBinding(IncreaseLarge, OnIncreaseLarge)); + CommandBindings.Add(new CommandBinding(IncreaseSmall, OnIncreaseSmall)); + + CommandBindings.Add(new CommandBinding(DecreaseLarge, OnDecreaseLarge)); + CommandBindings.Add(new CommandBinding(DecreaseSmall, OnDecreaseSmall)); + + CommandBindings.Add(new CommandBinding(CenterLarge, OnCenterLarge)); + CommandBindings.Add(new CommandBinding(CenterSmall, OnCenterSmall)); + } + + private void OnIncreaseLarge(object sender, ExecutedRoutedEventArgs e) => (sender as RangeSlider)?.OnIncreaseLarge(); + + private void OnIncreaseSmall(object sender, ExecutedRoutedEventArgs e) => (sender as RangeSlider)?.OnIncreaseSmall(); + + private void OnDecreaseLarge(object sender, ExecutedRoutedEventArgs e) => (sender as RangeSlider)?.OnDecreaseLarge(); + + private void OnDecreaseSmall(object sender, ExecutedRoutedEventArgs e) => (sender as RangeSlider)?.OnDecreaseSmall(); + + private void OnCenterLarge(object sender, ExecutedRoutedEventArgs e) => (sender as RangeSlider)?.OnCenterLarge(e.Parameter); + + private void OnCenterSmall(object sender, ExecutedRoutedEventArgs e) => (sender as RangeSlider)?.OnCenterSmall(e.Parameter); + + protected virtual void OnIncreaseLarge() => MoveToNextTick(LargeChange, false); + + protected virtual void OnIncreaseSmall() => MoveToNextTick(LargeChange, false); + + protected virtual void OnDecreaseLarge() => MoveToNextTick(-LargeChange, true); + + protected virtual void OnDecreaseSmall() => MoveToNextTick(-LargeChange, true); + + protected virtual void OnCenterLarge(object parameter) => MoveToNextTick(LargeChange, false, true); + + protected virtual void OnCenterSmall(object parameter) => MoveToNextTick(LargeChange, false, true); + + public static RoutedCommand IncreaseLarge { get; private set; } + + public static RoutedCommand IncreaseSmall { get; private set; } + + public static RoutedCommand DecreaseLarge { get; private set; } + + public static RoutedCommand DecreaseSmall { get; private set; } + + public static RoutedCommand CenterLarge { get; private set; } + + public static RoutedCommand CenterSmall { get; private set; } + + private static void InitializeCommands() + { + IncreaseLarge = new RoutedCommand(nameof(IncreaseLarge), typeof(RangeSlider)); + IncreaseSmall = new RoutedCommand(nameof(IncreaseSmall), typeof(RangeSlider)); + + DecreaseLarge = new RoutedCommand(nameof(DecreaseLarge), typeof(RangeSlider)); + DecreaseSmall = new RoutedCommand(nameof(DecreaseSmall), typeof(RangeSlider)); + + CenterLarge = new RoutedCommand(nameof(CenterLarge), typeof(RangeSlider)); + CenterSmall = new RoutedCommand(nameof(CenterSmall), typeof(RangeSlider)); + } + + public override void OnApplyTemplate() + { + _thumbCurrent = null; + + base.OnApplyTemplate(); + + _track = GetTemplateChild(ElementTrack) as RangeTrack; + + if (_autoToolTipStart != null) + { + _autoToolTipStart.PlacementTarget = _track?.ThumbStart; + } + + if (_autoToolTipEnd != null) + { + _autoToolTipEnd.PlacementTarget = _track?.ThumbEnd; + } + } + + public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( + "Orientation", typeof(Orientation), typeof(RangeSlider), new PropertyMetadata(default(Orientation))); + + public Orientation Orientation + { + get => (Orientation)GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + + public static readonly DependencyProperty IsDirectionReversedProperty = DependencyProperty.Register( + "IsDirectionReversed", typeof(bool), typeof(RangeSlider), new PropertyMetadata(ValueBoxes.FalseBox)); + + public bool IsDirectionReversed + { + get => (bool)GetValue(IsDirectionReversedProperty); + set => SetValue(IsDirectionReversedProperty, value); + } + + public static readonly DependencyProperty DelayProperty = RepeatButton.DelayProperty.AddOwner(typeof(RangeSlider), new FrameworkPropertyMetadata(GetKeyboardDelay())); + + public int Delay + { + get => (int)GetValue(DelayProperty); + set => SetValue(DelayProperty, value); + } + + internal static int GetKeyboardDelay() + { + var delay = SystemParameters.KeyboardDelay; + if (delay < 0 || delay > 3) + delay = 0; + return (delay + 1) * 250; + } + + public static readonly DependencyProperty IntervalProperty = RepeatButton.IntervalProperty.AddOwner(typeof(RangeSlider), new FrameworkPropertyMetadata(GetKeyboardSpeed())); + + public int Interval + { + get => (int)GetValue(IntervalProperty); + set => SetValue(IntervalProperty, value); + } + + internal static int GetKeyboardSpeed() + { + var speed = SystemParameters.KeyboardSpeed; + if (speed < 0 || speed > 31) + speed = 31; + return (31 - speed) * (400 - 1000 / 30) / 31 + 1000 / 30; + } + + public static readonly DependencyProperty AutoToolTipPlacementProperty = DependencyProperty.Register( + "AutoToolTipPlacement", typeof(AutoToolTipPlacement), typeof(RangeSlider), new PropertyMetadata(default(AutoToolTipPlacement))); + + public AutoToolTipPlacement AutoToolTipPlacement + { + get => (AutoToolTipPlacement)GetValue(AutoToolTipPlacementProperty); + set => SetValue(AutoToolTipPlacementProperty, value); + } + + public static readonly DependencyProperty AutoToolTipPrecisionProperty = DependencyProperty.Register( + "AutoToolTipPrecision", typeof(int), typeof(RangeSlider), new PropertyMetadata(ValueBoxes.Int0Box), + ValidateHelper.IsInRangeOfPosIntIncludeZero); + + public int AutoToolTipPrecision + { + get => (int)GetValue(AutoToolTipPrecisionProperty); + set => SetValue(AutoToolTipPrecisionProperty, value); + } + + public static readonly DependencyProperty IsSnapToTickEnabledProperty = DependencyProperty.Register( + "IsSnapToTickEnabled", typeof(bool), typeof(RangeSlider), new PropertyMetadata(ValueBoxes.FalseBox)); + + public bool IsSnapToTickEnabled + { + get => (bool)GetValue(IsSnapToTickEnabledProperty); + set => SetValue(IsSnapToTickEnabledProperty, value); + } + + public static readonly DependencyProperty TickPlacementProperty = DependencyProperty.Register( + "TickPlacement", typeof(TickPlacement), typeof(RangeSlider), new PropertyMetadata(default(TickPlacement))); + + public TickPlacement TickPlacement + { + get => (TickPlacement)GetValue(TickPlacementProperty); + set => SetValue(TickPlacementProperty, value); + } + + public static readonly DependencyProperty TickFrequencyProperty = DependencyProperty.Register( + "TickFrequency", typeof(double), typeof(RangeSlider), new PropertyMetadata(ValueBoxes.Double1Box), + ValidateHelper.IsInRangeOfPosDoubleIncludeZero); + + public double TickFrequency + { + get => (double)GetValue(TickFrequencyProperty); + set => SetValue(TickFrequencyProperty, value); + } + + public static readonly DependencyProperty TicksProperty = DependencyProperty.Register( + "Ticks", typeof(DoubleCollection), typeof(RangeSlider), new PropertyMetadata(new DoubleCollection())); + + public DoubleCollection Ticks + { + get => (DoubleCollection)GetValue(TicksProperty); + set => SetValue(TicksProperty, value); + } + + public static readonly DependencyProperty IsMoveToPointEnabledProperty = DependencyProperty.Register( + "IsMoveToPointEnabled", typeof(bool), typeof(RangeSlider), new PropertyMetadata(ValueBoxes.FalseBox)); + + public bool IsMoveToPointEnabled + { + get => (bool)GetValue(IsMoveToPointEnabledProperty); + set => SetValue(IsMoveToPointEnabledProperty, value); + } + + protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (IsMoveToPointEnabled && _track.ThumbStart != null && !_track.ThumbStart.IsMouseOver && _track.ThumbEnd != null && !_track.ThumbEnd.IsMouseOver) + { + // Here we need to determine whether it's closer to the starting point or the end point. + var pt = e.MouseDevice.GetPosition(_track); + UpdateValue(pt); + e.Handled = true; + } + + base.OnPreviewMouseLeftButtonDown(e); + } + + private void MoveToNextTick(double direction, bool isStart, bool isCenter = false, object parameter = null) + { + if (MathHelper.AreClose(direction, 0)) return; + + if (isCenter) + { + if (parameter == null) + { + var pt = Mouse.GetPosition(_track); + var newValue = _track.ValueFromPoint(pt); + if (ValidateHelper.IsInRangeOfDouble(newValue)) + { + var dir = (ValueStart + ValueEnd) / 2 > newValue; + if (!dir) + { + isStart = false; + direction = -Math.Abs(direction); + } + else + { + isStart = true; + } + } + } + + if (parameter is bool parameterValue) + { + isStart = parameterValue; + } + } + + var value = isStart ? ValueStart : ValueEnd; + var next = SnapToTick(Math.Max(Minimum, Math.Min(Maximum, value + direction))); + var greaterThan = direction > 0; + + // If the snapping brought us back to value, find the next tick point + if (MathHelper.AreClose(next, value) && + !(greaterThan && MathHelper.AreClose(value, Maximum)) && + !(!greaterThan && MathHelper.AreClose(value, Minimum))) + { + // If ticks collection is available, use it. + // Note that ticks may be unsorted. + if (Ticks != null && Ticks.Count > 0) + { + foreach (var tick in Ticks) + { + // Find the smallest tick greater than value or the largest tick less than value + if (greaterThan && MathHelper.GreaterThan(tick, value) && (MathHelper.LessThan(tick, next) || MathHelper.AreClose(next, value)) + || !greaterThan && MathHelper.LessThan(tick, value) && (MathHelper.GreaterThan(tick, next) || MathHelper.AreClose(next, value))) + { + next = tick; + } + } + } + else if (MathHelper.GreaterThan(TickFrequency, 0.0)) + { + // Find the current tick we are at + var tickNumber = Math.Round((value - Minimum) / TickFrequency); + + if (greaterThan) + tickNumber += 1.0; + else + tickNumber -= 1.0; + + next = Minimum + tickNumber * TickFrequency; + } + } + + // Update if we've found a better value + if (!MathHelper.AreClose(next, value)) + { + SetCurrentValue(isStart ? ValueStartProperty : ValueEndProperty, next); + } + } + + private double SnapToTick(double value) + { + if (!IsSnapToTickEnabled) return value; + + var previous = Minimum; + var next = Maximum; + + if (Ticks != null && Ticks.Count > 0) + { + foreach (var tick in Ticks) + { + if (MathHelper.AreClose(tick, value)) + { + return value; + } + + if (MathHelper.LessThan(tick, value) && MathHelper.GreaterThan(tick, previous)) + { + previous = tick; + } + else if (MathHelper.GreaterThan(tick, value) && MathHelper.LessThan(tick, next)) + { + next = tick; + } + } + } + else if (MathHelper.GreaterThan(TickFrequency, 0.0)) + { + previous = Minimum + Math.Round((value - Minimum) / TickFrequency) * TickFrequency; + next = Math.Min(Maximum, previous + TickFrequency); + } + + return MathHelper.GreaterThanOrClose(value, (previous + next) * 0.5) ? next : previous; + } + + private void UpdateValue(Point point) + { + var newValue = _track.ValueFromPoint(point); + if (ValidateHelper.IsInRangeOfDouble(newValue)) + { + var isStart = (ValueStart + ValueEnd) / 2 > newValue; + UpdateValue(newValue, isStart); + } + } + + private void UpdateValue(double value, bool isStart) + { + var snappedValue = SnapToTick(value); + + if (isStart) + { + if (!MathHelper.AreClose(snappedValue, ValueStart)) + { + var start = Math.Max(Minimum, Math.Min(Maximum, snappedValue)); + if (start > ValueEnd) + { + SetCurrentValue(ValueEndProperty, start); + _track.ThumbStart.CancelDrag(); + _track.ThumbEnd.StartDrag(); + _thumbCurrent = _track.ThumbEnd; + } + else + { + SetCurrentValue(ValueStartProperty, start); + } + } + } + else + { + if (!MathHelper.AreClose(snappedValue, ValueEnd)) + { + var end = Math.Max(Minimum, Math.Min(Maximum, snappedValue)); + if (end < ValueStart) + { + SetCurrentValue(ValueStartProperty, end); + _track.ThumbEnd.CancelDrag(); + _track.ThumbStart.StartDrag(); + _thumbCurrent = _track.ThumbStart; + } + else + { + SetCurrentValue(ValueEndProperty, end); + } + } + } + } + + private static void OnThumbDragStarted(object sender, DragStartedEventArgs e) => (sender as RangeSlider)?.OnThumbDragStarted(e); + + protected virtual void OnThumbDragStarted(DragStartedEventArgs e) + { + // Show AutoToolTip if needed. + + if (!(e.OriginalSource is RangeThumb thumb)) return; + _thumbCurrent = thumb; + _originThumbPoint = Mouse.GetPosition(_thumbCurrent); + _thumbCurrent.StartDrag(); + + if (AutoToolTipPlacement == AutoToolTipPlacement.None) + { + return; + } + + var isStart = thumb.Equals(_track.ThumbStart); + if (!isStart) + { + if (!thumb.Equals(_track.ThumbEnd)) return; + } + + // Save original tooltip + _thumbOriginalToolTip = thumb.ToolTip; + OnThumbDragStarted(isStart ? _autoToolTipStart : _autoToolTipEnd, isStart); + } + + private void OnThumbDragStarted(ToolTip toolTip, bool isStart) + { + if (toolTip == null) + { + toolTip = new ToolTip + { + Placement = PlacementMode.Custom, + PlacementTarget = isStart ? _track.ThumbStart : _track.ThumbEnd, + CustomPopupPlacementCallback = AutoToolTipCustomPlacementCallback + }; + } + + if (isStart) + { + _track.ThumbStart.ToolTip = toolTip; + } + else + { + _track.ThumbEnd.ToolTip = toolTip; + } + toolTip.Content = GetAutoToolTipNumber(isStart); + toolTip.IsOpen = true; + } + + private CustomPopupPlacement[] AutoToolTipCustomPlacementCallback(Size popupSize, Size targetSize, Point offset) + { + switch (AutoToolTipPlacement) + { + case AutoToolTipPlacement.TopLeft: + if (Orientation == Orientation.Horizontal) + { + // Place popup at top of thumb + return new[]{new CustomPopupPlacement( + new Point((targetSize.Width - popupSize.Width) * 0.5, -popupSize.Height), + PopupPrimaryAxis.Horizontal) + }; + } + else + { + // Place popup at left of thumb + return new[] { + new CustomPopupPlacement( + new Point(-popupSize.Width, (targetSize.Height - popupSize.Height) * 0.5), + PopupPrimaryAxis.Vertical) + }; + } + + case AutoToolTipPlacement.BottomRight: + if (Orientation == Orientation.Horizontal) + { + // Place popup at bottom of thumb + return new[] { + new CustomPopupPlacement( + new Point((targetSize.Width - popupSize.Width) * 0.5, targetSize.Height) , + PopupPrimaryAxis.Horizontal) + }; + + } + else + { + // Place popup at right of thumb + return new[] { + new CustomPopupPlacement( + new Point(targetSize.Width, (targetSize.Height - popupSize.Height) * 0.5), + PopupPrimaryAxis.Vertical) + }; + } + + default: + return new CustomPopupPlacement[] { }; + } + } + + private string GetAutoToolTipNumber(bool isStart) + { + var format = (NumberFormatInfo)NumberFormatInfo.CurrentInfo.Clone(); + format.NumberDecimalDigits = AutoToolTipPrecision; + return isStart ? ValueStart.ToString("N", format) : ValueEnd.ToString("N", format); + } + + private static void OnThumbDragDelta(object sender, DragDeltaEventArgs e) => (sender as RangeSlider)?.OnThumbDragDelta(e); + + protected virtual void OnThumbDragDelta(DragDeltaEventArgs e) + { + if (!(e.OriginalSource is Thumb thumb)) return; + var isStart = thumb.Equals(_track.ThumbStart); + if (!isStart) + { + if (!thumb.Equals(_track.ThumbEnd)) return; + } + + // Convert to Track's co-ordinate + OnThumbDragDelta(_track, isStart, e); + } + + private void OnThumbDragDelta(RangeTrack track, bool isStart, DragDeltaEventArgs e) + { + if (track == null || track.ThumbStart == null | _track.ThumbEnd == null) return; + + var newValue = (isStart ? ValueStart : ValueEnd) + track.ValueFromDistance(e.HorizontalChange, e.VerticalChange); + Console.WriteLine(newValue); + if (ValidateHelper.IsInRangeOfDouble(newValue)) + { + UpdateValue(newValue, isStart); + } + + // Show AutoToolTip if needed + if (AutoToolTipPlacement != AutoToolTipPlacement.None) + { + var toolTip = (isStart ? _autoToolTipStart : _autoToolTipEnd) ?? new ToolTip(); + + toolTip.Content = GetAutoToolTipNumber(isStart); + + var thumb = isStart ? _track.ThumbStart : _track.ThumbEnd; + + if (!Equals(thumb.ToolTip, toolTip)) + { + thumb.ToolTip = toolTip; + } + + if (!toolTip.IsOpen) + { + toolTip.IsOpen = true; + } + } + } + + private static void OnThumbDragCompleted(object sender, DragCompletedEventArgs e) => (sender as RangeSlider)?.OnThumbDragCompleted(e); + + protected virtual void OnThumbDragCompleted(DragCompletedEventArgs e) + { + // Show AutoToolTip if needed. + + if (!(e.OriginalSource is Thumb thumb) || AutoToolTipPlacement == AutoToolTipPlacement.None) + { + return; + } + + var isStart = thumb.Equals(_track.ThumbStart); + if (!isStart) + { + if (!thumb.Equals(_track.ThumbEnd)) return; + } + + var toolTip = isStart ? _autoToolTipStart : _autoToolTipEnd; + if (toolTip != null) + { + toolTip.IsOpen = false; + } + + thumb.ToolTip = _thumbOriginalToolTip; + } + + private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton != MouseButton.Left) return; + + var slider = (RangeSlider)sender; + + // When someone click on the Slider's part, and it's not focusable + // Slider need to take the focus in order to process keyboard correctly + if (!slider.IsKeyboardFocusWithin) + { + e.Handled = slider.Focus() || e.Handled; + } + + if (slider._track.ThumbStart.IsMouseOver) + { + slider._track.ThumbStart.StartDrag(); + slider._thumbCurrent = slider._track.ThumbStart; + } + + if (slider._track.ThumbEnd.IsMouseOver) + { + slider._track.ThumbEnd.StartDrag(); + slider._thumbCurrent = slider._track.ThumbEnd; + } + } + + protected override void OnMouseMove(MouseEventArgs e) + { + Console.WriteLine(e.GetPosition(this)); + if (_thumbCurrent == null) return; + if (e.MouseDevice.LeftButton != MouseButtonState.Pressed) return; + + if (!_thumbCurrent.IsDragging) return; + + var thumbCoordPosition = e.GetPosition(_thumbCurrent); + var screenCoordPosition = PointFromScreen(thumbCoordPosition); + if (screenCoordPosition != _previousScreenCoordPosition) + { + _previousScreenCoordPosition = screenCoordPosition; + _thumbCurrent.RaiseEvent(new DragDeltaEventArgs(thumbCoordPosition.X - _originThumbPoint.X, + thumbCoordPosition.Y - _originThumbPoint.Y)); + } + } + + protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) + { + base.OnPreviewMouseLeftButtonUp(e); + + _thumbCurrent = null; + } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeThumb.cs b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeThumb.cs new file mode 100644 index 00000000..fee42007 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeThumb.cs @@ -0,0 +1,46 @@ +using System; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace HandyControl.Controls +{ + public class RangeThumb : Thumb + { + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( + "Content", typeof(object), typeof(RangeThumb), new PropertyMetadata(default(object))); + + public object Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + + } + + public void StartDrag() + { + IsDragging = true; + Focus(); + CaptureMouse(); + RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left) + { + RoutedEvent = PreviewMouseLeftButtonDownEvent, + Source = this + }); + } + + public new void CancelDrag() + { + base.CancelDrag(); + + RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left) + { + RoutedEvent = PreviewMouseLeftButtonUpEvent + }); + } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeTrack.cs b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeTrack.cs new file mode 100644 index 00000000..99e4dfad --- /dev/null +++ b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/RangeTrack.cs @@ -0,0 +1,444 @@ +// Adapted from https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/Track.cs + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using HandyControl.Data; + +namespace HandyControl.Controls +{ + public class RangeTrack : FrameworkElement + { + private RepeatButton _increaseButton; + + private RepeatButton _centerButton; + + private RepeatButton _decreaseButton; + + private RangeThumb _thumbStart; + + private RangeThumb _thumbEnd; + + private Visual[] _visualChildren; + + private double Density { get; set; } = double.NaN; + + private double ThumbStartCenterOffset { get; set; } = double.NaN; + + public RepeatButton DecreaseRepeatButton + { + get => _decreaseButton; + set + { + if (Equals(_increaseButton, value) || Equals(_centerButton, value)) + { + throw new NotSupportedException("SameButtons"); + } + UpdateComponent(_decreaseButton, value); + _decreaseButton = value; + + if (_decreaseButton != null) + { + CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button + } + } + } + + public RepeatButton CenterRepeatButton + { + get => _centerButton; + set + { + if (Equals(_increaseButton, value) || Equals(_decreaseButton, value)) + { + throw new NotSupportedException("SameButtons"); + } + UpdateComponent(_centerButton, value); + _centerButton = value; + + if (_centerButton != null) + { + CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button + } + } + } + + public RepeatButton IncreaseRepeatButton + { + get => _increaseButton; + set + { + if (Equals(_decreaseButton, value) || Equals(_centerButton, value)) + { + throw new NotSupportedException("SameButtons"); + } + UpdateComponent(_increaseButton, value); + _increaseButton = value; + + if (_increaseButton != null) + { + CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on button + } + } + } + + public RangeThumb ThumbStart + { + get => _thumbStart; + set + { + UpdateComponent(_thumbStart, value); + _thumbStart = value; + } + } + + public RangeThumb ThumbEnd + { + get => _thumbEnd; + set + { + UpdateComponent(_thumbEnd, value); + _thumbEnd = value; + } + } + + static RangeTrack() + { + IsEnabledProperty.OverrideMetadata(typeof(RangeTrack), new UIPropertyMetadata(OnIsEnabledChanged)); + } + + public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register( + "Orientation", typeof(Orientation), typeof(RangeTrack), + new FrameworkPropertyMetadata(default(Orientation), FrameworkPropertyMetadataOptions.AffectsMeasure)); + + public Orientation Orientation + { + get => (Orientation) GetValue(OrientationProperty); + set => SetValue(OrientationProperty, value); + } + + public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( + "Minimum", typeof(double), typeof(RangeTrack), + new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsArrange)); + + public double Minimum + { + get => (double) GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); + } + + public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( + "Maximum", typeof(double), typeof(RangeTrack), + new FrameworkPropertyMetadata(ValueBoxes.Double1Box, FrameworkPropertyMetadataOptions.AffectsArrange)); + + public double Maximum + { + get => (double) GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); + } + + public static readonly DependencyProperty ValueStartProperty = DependencyProperty.Register( + "ValueStart", typeof(double), typeof(RangeTrack), + new FrameworkPropertyMetadata(ValueBoxes.Double0Box, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | + FrameworkPropertyMetadataOptions.AffectsArrange)); + + public double ValueStart + { + get => (double) GetValue(ValueStartProperty); + set => SetValue(ValueStartProperty, value); + } + + public static readonly DependencyProperty ValueEndProperty = DependencyProperty.Register( + "ValueEnd", typeof(double), typeof(RangeTrack), + new FrameworkPropertyMetadata(ValueBoxes.Double0Box, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | + FrameworkPropertyMetadataOptions.AffectsArrange)); + + public double ValueEnd + { + get => (double) GetValue(ValueEndProperty); + set => SetValue(ValueEndProperty, value); + } + + public static readonly DependencyProperty IsDirectionReversedProperty = DependencyProperty.Register( + "IsDirectionReversed", typeof(bool), typeof(RangeTrack), new PropertyMetadata(ValueBoxes.FalseBox)); + + public bool IsDirectionReversed + { + get => (bool) GetValue(IsDirectionReversedProperty); + set => SetValue(IsDirectionReversedProperty, value); + } + + protected override Visual GetVisualChild(int index) + { + if (_visualChildren?[index] == null) + { + // ReSharper disable once UseNameofExpression + // ReSharper disable once LocalizableElement + throw new ArgumentOutOfRangeException("index", index, "ArgumentOutOfRange"); + } + return _visualChildren[index]; + } + + protected override Size MeasureOverride(Size availableSize) + { + var desiredSize = new Size(); + + // Only measure thumb + // Repeat buttons will be sized based on thumb + if (_thumbStart != null) + { + _thumbStart.Measure(availableSize); + desiredSize = _thumbStart.DesiredSize; + } + + if (_thumbEnd != null) + { + _thumbEnd.Measure(availableSize); + desiredSize = new Size(Math.Max(_thumbEnd.DesiredSize.Width, desiredSize.Width), + Math.Max(_thumbEnd.DesiredSize.Height, desiredSize.Height)); + } + + return desiredSize; + } + + private static void CoerceLength(ref double componentLength, double trackLength) + { + if (componentLength < 0) + { + componentLength = 0; + } + else if (componentLength > trackLength || double.IsNaN(componentLength)) + { + componentLength = trackLength; + } + } + + protected override Size ArrangeOverride(Size arrangeSize) + { + var isVertical = Orientation == Orientation.Vertical; + + ComputeLengths(arrangeSize, isVertical, out var decreaseButtonLength, out var centerButtonLength, + out var increaseButtonLength, out var thumbStartLength, out var thumbEndLength); + + var offset = new Point(); + var pieceSize = arrangeSize; + var isDirectionReversed = IsDirectionReversed; + + if (isVertical) + { + CoerceLength(ref decreaseButtonLength, arrangeSize.Height); + CoerceLength(ref centerButtonLength, arrangeSize.Height); + CoerceLength(ref increaseButtonLength, arrangeSize.Height); + CoerceLength(ref thumbStartLength, arrangeSize.Height); + CoerceLength(ref thumbEndLength, arrangeSize.Height); + + offset.Y = isDirectionReversed ? decreaseButtonLength + thumbEndLength + centerButtonLength + thumbStartLength : 0; + pieceSize.Height = increaseButtonLength; + + IncreaseRepeatButton?.Arrange(new Rect(offset, pieceSize)); + + offset.Y = isDirectionReversed ? decreaseButtonLength + thumbEndLength : increaseButtonLength + thumbStartLength; + pieceSize.Height = centerButtonLength; + + CenterRepeatButton?.Arrange(new Rect(offset, pieceSize)); + + offset.Y = isDirectionReversed ? 0 : increaseButtonLength + thumbStartLength + centerButtonLength + thumbEndLength; + pieceSize.Height = decreaseButtonLength; + + DecreaseRepeatButton?.Arrange(new Rect(offset, pieceSize)); + + offset.Y = isDirectionReversed + ? decreaseButtonLength + thumbEndLength + centerButtonLength + : increaseButtonLength + thumbStartLength + centerButtonLength; + pieceSize.Height = thumbStartLength; + + ThumbStart?.Arrange(new Rect(offset, pieceSize)); + ThumbStartCenterOffset = offset.Y + thumbStartLength * 0.5; + + offset.Y = isDirectionReversed ? decreaseButtonLength : increaseButtonLength; + pieceSize.Height = thumbEndLength; + + ThumbEnd?.Arrange(new Rect(offset, pieceSize)); + } + else + { + CoerceLength(ref decreaseButtonLength, arrangeSize.Width); + CoerceLength(ref centerButtonLength, arrangeSize.Width); + CoerceLength(ref increaseButtonLength, arrangeSize.Width); + CoerceLength(ref thumbStartLength, arrangeSize.Width); + CoerceLength(ref thumbEndLength, arrangeSize.Width); + + offset.X = isDirectionReversed ? 0 : decreaseButtonLength + thumbEndLength + centerButtonLength + thumbStartLength; + pieceSize.Width = increaseButtonLength; + + IncreaseRepeatButton?.Arrange(new Rect(offset, pieceSize)); + + offset.X = isDirectionReversed ? increaseButtonLength + thumbStartLength : decreaseButtonLength + thumbEndLength; + pieceSize.Width = centerButtonLength; + + CenterRepeatButton?.Arrange(new Rect(offset, pieceSize)); + + offset.X = isDirectionReversed ? increaseButtonLength + thumbStartLength + centerButtonLength + thumbEndLength : 0; + pieceSize.Width = decreaseButtonLength; + + DecreaseRepeatButton?.Arrange(new Rect(offset, pieceSize)); + + offset.X = isDirectionReversed ? increaseButtonLength : decreaseButtonLength; + pieceSize.Width = thumbStartLength; + + ThumbStart?.Arrange(new Rect(offset, pieceSize)); + ThumbStartCenterOffset = offset.X + thumbStartLength * 0.5; + + offset.X = isDirectionReversed + ? increaseButtonLength + thumbStartLength + centerButtonLength + : decreaseButtonLength + thumbEndLength + centerButtonLength; + pieceSize.Width = thumbEndLength; + + ThumbEnd?.Arrange(new Rect(offset, pieceSize)); + } + + return arrangeSize; + } + + private void ComputeLengths(Size arrangeSize, bool isVertical, out double decreaseButtonLength, + out double centerButtonLength, out double increaseButtonLength, out double thumbStartLength, + out double thumbEndLength) + { + var min = Minimum; + var range = Math.Max(0.0, Maximum - min); + var offsetStart = Math.Min(range, ValueStart - min); + var offsetEnd = Math.Min(range, ValueEnd - min); + + double trackLength; + + // Compute thumb size + if (isVertical) + { + trackLength = arrangeSize.Height; + thumbStartLength = _thumbStart?.DesiredSize.Height ?? 0; + thumbEndLength = _thumbEnd?.DesiredSize.Height ?? 0; + } + else + { + trackLength = arrangeSize.Width; + thumbStartLength = _thumbStart?.DesiredSize.Width ?? 0; + thumbEndLength = _thumbEnd?.DesiredSize.Width ?? 0; + } + + CoerceLength(ref thumbStartLength, trackLength); + CoerceLength(ref thumbEndLength, trackLength); + + var remainingTrackLength = trackLength - thumbStartLength - thumbEndLength; + + decreaseButtonLength = remainingTrackLength * offsetStart / range; + CoerceLength(ref decreaseButtonLength, remainingTrackLength); + + centerButtonLength = remainingTrackLength * offsetEnd / range - decreaseButtonLength; + CoerceLength(ref centerButtonLength, remainingTrackLength); + + increaseButtonLength = remainingTrackLength - decreaseButtonLength - centerButtonLength; + CoerceLength(ref increaseButtonLength, remainingTrackLength); + + Density = range / remainingTrackLength; + } + + protected override int VisualChildrenCount + { + get + { + if (_visualChildren == null) return 0; + + for (int i = 0; i < _visualChildren.Length; i++) + { + if (_visualChildren[i] == null) return i; + } + + return _visualChildren.Length; + } + } + + private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if ((bool)e.NewValue) + { + Mouse.Synchronize(); + } + } + + public virtual double ValueFromPoint(Point pt) + { + double val; + // Find distance from center of thumb to given point. + if (Orientation == Orientation.Horizontal) + { + val = ValueStart + ValueFromDistance(pt.X - ThumbStartCenterOffset, pt.Y - RenderSize.Height * 0.5); + } + else + { + val = ValueStart + ValueFromDistance(pt.X - RenderSize.Width * 0.5, pt.Y - ThumbStartCenterOffset); + } + return Math.Max(Minimum, Math.Min(Maximum, val)); + } + + public virtual double ValueFromDistance(double horizontal, double vertical) + { + double scale = IsDirectionReversed ? -1 : 1; + return Orientation == Orientation.Horizontal + ? scale * horizontal * Density + : -1 * scale * vertical * Density; + } + + private void UpdateComponent(Control oldValue, Control newValue) + { + if (oldValue != newValue) + { + if (_visualChildren == null) + { + _visualChildren = new Visual[5]; + } + + if (oldValue != null) + { + // notify the visual layer that the old component has been removed. + RemoveVisualChild(oldValue); + } + + // Remove the old value from our z index list and add new value to end + int i = 0; + while (i < 5) + { + // Array isn't full, break + if (_visualChildren[i] == null) + break; + + // found the old value + if (_visualChildren[i] == oldValue) + { + // Move values down until end of array or a null element + while (i < 4 && _visualChildren[i + 1] != null) + { + _visualChildren[i] = _visualChildren[i + 1]; + i++; + } + } + else + { + i++; + } + } + // Add newValue at end of z-order + _visualChildren[i] = newValue; + + AddVisualChild(newValue); + + InvalidateMeasure(); + InvalidateArrange(); + } + } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs new file mode 100644 index 00000000..7c3b5b5f --- /dev/null +++ b/src/Shared/HandyControl_Shared/Controls/Slider/RangeSlider/TwoWayRangeBase.cs @@ -0,0 +1,181 @@ +// Adapted from https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/RangeBase.cs + +using HandyControl.Data; +using HandyControl.Tools; +using System.Windows; +using System.Windows.Controls; + +namespace HandyControl.Controls +{ + public class TwoWayRangeBase : Control + { + public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( + "Minimum", typeof(double), typeof(TwoWayRangeBase), + new PropertyMetadata(ValueBoxes.Double0Box, OnMinimumChanged), ValidateHelper.IsInRangeOfDouble); + + private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (TwoWayRangeBase)d; + + ctrl.CoerceValue(MaximumProperty); + ctrl.CoerceValue(ValueStartProperty); + ctrl.CoerceValue(ValueEndProperty); + ctrl.OnMinimumChanged((double)e.OldValue, (double)e.NewValue); + } + + protected virtual void OnMinimumChanged(double oldMinimum, double newMinimum) + { + } + + public double Minimum + { + get => (double)GetValue(MinimumProperty); + set => SetValue(MinimumProperty, value); + } + + public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( + "Maximum", typeof(double), typeof(TwoWayRangeBase), + new PropertyMetadata(ValueBoxes.Double10Box, OnMaximumChanged, CoerceMaximum), + ValidateHelper.IsInRangeOfDouble); + + private static object CoerceMaximum(DependencyObject d, object basevalue) + { + var ctrl = (TwoWayRangeBase)d; + var min = ctrl.Minimum; + if ((double)basevalue < min) + { + return min; + } + return basevalue; + } + + private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (TwoWayRangeBase)d; + + ctrl.CoerceValue(ValueStartProperty); + ctrl.CoerceValue(ValueEndProperty); + ctrl.OnMaximumChanged((double)e.OldValue, (double)e.NewValue); + } + + protected virtual void OnMaximumChanged(double oldMaximum, double newMaximum) + { + } + + public double Maximum + { + get => (double)GetValue(MaximumProperty); + set => SetValue(MaximumProperty, value); + } + + public static readonly DependencyProperty ValueStartProperty = DependencyProperty.Register( + "ValueStart", typeof(double), typeof(TwoWayRangeBase), + new FrameworkPropertyMetadata(ValueBoxes.Double0Box, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, + OnValueStartChanged, ConstrainToRange), ValidateHelper.IsInRangeOfDouble); + + private static void OnValueStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (TwoWayRangeBase)d; + ctrl.OnValueChanged(new TwoWayRange + { + ValueStart = (double)e.OldValue, + ValueEnd = ctrl.ValueEnd + }, new TwoWayRange + { + ValueStart = (double)e.NewValue, + ValueEnd = ctrl.ValueEnd + }); + } + + protected virtual void OnValueChanged(TwoWayRange oldValue, TwoWayRange newValue) => RaiseEvent( + new RoutedPropertyChangedEventArgs(oldValue, newValue) {RoutedEvent = ValueChangedEvent}); + + public double ValueStart + { + get => (double)GetValue(ValueStartProperty); + set => SetValue(ValueStartProperty, value); + } + + public static readonly DependencyProperty ValueEndProperty = DependencyProperty.Register( + "ValueEnd", typeof(double), typeof(TwoWayRangeBase), + new FrameworkPropertyMetadata(ValueBoxes.Double0Box, + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, + OnValueEndChanged, ConstrainToRange), ValidateHelper.IsInRangeOfDouble); + + private static void OnValueEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ctrl = (TwoWayRangeBase)d; + ctrl.OnValueChanged(new TwoWayRange + { + ValueStart = ctrl.ValueStart, + ValueEnd = (double)e.OldValue + }, new TwoWayRange + { + ValueStart = ctrl.ValueStart, + ValueEnd = (double)e.NewValue + }); + } + + public double ValueEnd + { + get => (double)GetValue(ValueEndProperty); + set => SetValue(ValueEndProperty, value); + } + + internal static object ConstrainToRange(DependencyObject d, object value) + { + var ctrl = (TwoWayRangeBase)d; + var min = ctrl.Minimum; + var v = (double)value; + if (v < min) + { + return min; + } + + var max = ctrl.Maximum; + if (v > max) + { + return max; + } + + return value; + } + + public static readonly DependencyProperty LargeChangeProperty = DependencyProperty.Register( + "LargeChange", typeof(double), typeof(TwoWayRangeBase), new PropertyMetadata(ValueBoxes.Double1Box), + ValidateHelper.IsInRangeOfPosDoubleIncludeZero); + + public double LargeChange + { + get => (double)GetValue(LargeChangeProperty); + set => SetValue(LargeChangeProperty, value); + } + + public static readonly DependencyProperty SmallChangeProperty = DependencyProperty.Register( + "SmallChange", typeof(double), typeof(TwoWayRangeBase), new PropertyMetadata(ValueBoxes.Double01Box), + ValidateHelper.IsInRangeOfPosDoubleIncludeZero); + + public double SmallChange + { + get => (double)GetValue(SmallChangeProperty); + set => SetValue(SmallChangeProperty, value); + } + + public static readonly RoutedEvent ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", + RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(TwoWayRangeBase)); + + public event RoutedPropertyChangedEventHandler ValueChanged + { + add => AddHandler(ValueChangedEvent, value); + remove => RemoveHandler(ValueChangedEvent, value); + } + } + + public struct TwoWayRange + { + public double ValueStart { get; set; } + + public double ValueEnd { get; set; } + } +} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Data/ValueBoxes.cs b/src/Shared/HandyControl_Shared/Data/ValueBoxes.cs index d53cba75..dea64e0b 100644 --- a/src/Shared/HandyControl_Shared/Data/ValueBoxes.cs +++ b/src/Shared/HandyControl_Shared/Data/ValueBoxes.cs @@ -11,8 +11,12 @@ internal static object Double0Box = .0; + internal static object Double01Box = .1; + internal static object Double1Box = 1.0; + internal static object Double10Box = 10.0; + internal static object Double20Box = 20.0; internal static object Double100Box = 100.0; diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index ca096912..4b26b7e4 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -18,6 +18,9 @@ + + + @@ -85,10 +88,10 @@ - - + + - + @@ -333,6 +336,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Base/ColorPickerBaseStyle.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Base/ColorPickerBaseStyle.xaml new file mode 100644 index 00000000..62f6a76b --- /dev/null +++ b/src/Shared/HandyControl_Shared/Themes/Styles/Base/ColorPickerBaseStyle.xaml @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/Base/SliderBaseStyle.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/Base/SliderBaseStyle.xaml index 0fc570c5..aac15bce 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/Base/SliderBaseStyle.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/Base/SliderBaseStyle.xaml @@ -9,6 +9,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Themes/Styles/ColorPicker.xaml b/src/Shared/HandyControl_Shared/Themes/Styles/ColorPicker.xaml index 2adc223b..cd591fc8 100644 --- a/src/Shared/HandyControl_Shared/Themes/Styles/ColorPicker.xaml +++ b/src/Shared/HandyControl_Shared/Themes/Styles/ColorPicker.xaml @@ -1,126 +1,10 @@  + xmlns:controls="clr-namespace:HandyControl.Controls"> - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/ArithmeticHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/ArithmeticHelper.cs index d6990e2b..6dffd905 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/ArithmeticHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/ArithmeticHelper.cs @@ -36,9 +36,9 @@ namespace HandyControl.Tools /// /// 计算控件在窗口中的可见坐标 /// - public static Point CalSafePoint(FrameworkElement element, FrameworkElement showElement, Thickness thickness = default(Thickness)) + public static Point CalSafePoint(FrameworkElement element, FrameworkElement showElement, Thickness thickness = default) { - if (element == null || showElement == null) return default(Point); + if (element == null || showElement == null) return default; var point = element.PointToScreen(new Point(0, 0)); if (point.X < 0) point.X = 0; @@ -122,19 +122,6 @@ namespace HandyControl.Tools /// public static double CalAngle(Point center, Point p) => Math.Atan2(p.Y - center.Y, p.X - center.X) * 180 / Math.PI; - /// - /// 判断是否是有效的双精度浮点数 - /// - /// - /// - public static bool IsValidDoubleValue(object value) - { - var d = (double)value; - if (!double.IsNaN(d)) - return !double.IsInfinity(d); - return false; - } - /// /// 计算法线 /// diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/ValidateHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/ValidateHelper.cs index ff69c094..b5c7a21a 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/ValidateHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/ValidateHelper.cs @@ -20,24 +20,88 @@ /// 是否在正浮点数范围内 /// /// - /// /// - public static bool IsInRangeOfPosDouble(object value, bool includeZero = false) + public static bool IsInRangeOfPosDouble(object value) { var v = (double)value; - return !(double.IsNaN(v) || double.IsInfinity(v)) && (includeZero ? v >= 0 : v > 0); + return !(double.IsNaN(v) || double.IsInfinity(v)) && v > 0; + } + + /// + /// 是否在正浮点数范围内(包括0) + /// + /// + /// + public static bool IsInRangeOfPosDoubleIncludeZero(object value) + { + var v = (double)value; + return !(double.IsNaN(v) || double.IsInfinity(v)) && v >= 0; } /// /// 是否在负浮点数范围内 /// /// - /// /// - public static bool IsInRangeOfNegDouble(object value, bool includeZero = false) + public static bool IsInRangeOfNegDouble(object value) { var v = (double)value; - return !(double.IsNaN(v) || double.IsInfinity(v)) && (includeZero ? v <= 0 : v < 0); + return !(double.IsNaN(v) || double.IsInfinity(v)) && v < 0; + } + + /// + /// 是否在负浮点数范围内(包括0) + /// + /// + /// + public static bool IsInRangeOfNegDoubleIncludeZero(object value) + { + var v = (double)value; + return !(double.IsNaN(v) || double.IsInfinity(v)) && v <= 0; + } + + /// + /// 是否在正整数范围内 + /// + /// + /// + public static bool IsInRangeOfPosInt(object value) + { + var v = (int)value; + return v > 0; + } + + /// + /// 是否在正整数范围内(包括0) + /// + /// + /// + public static bool IsInRangeOfPosIntIncludeZero(object value) + { + var v = (int)value; + return v >= 0; + } + + /// + /// 是否在负整数范围内 + /// + /// + /// + public static bool IsInRangeOfNegInt(object value) + { + var v = (int)value; + return v < 0; + } + + /// + /// 是否在负整数范围内(包括0) + /// + /// + /// + public static bool IsInRangeOfNegIntIncludeZero(object value) + { + var v = (int)value; + return v <= 0; } } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs index 5ba5ae07..363478e0 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs @@ -42,7 +42,7 @@ namespace HandyControl.Tools if (result != null) return result; } - return default(T); + return default; } ///