Merge branch 'master' into pr/84
This commit is contained in:
commit
c757ebac53
166 files changed
+4558
-1259
No files matched your search
@@ -246,7 +246,7 @@ namespace HandyControl.Controls
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
for (int i = 0; i < itemsCount; i++)
|
||||
for (var i = 0; i < itemsCount; i++)
|
||||
{
|
||||
var row = (DataGridRow)generator.ContainerFromIndex(i);
|
||||
if (row != null)
|
||||
@@ -257,7 +257,7 @@ namespace HandyControl.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < itemsCount; i++)
|
||||
for (var i = 0; i < itemsCount; i++)
|
||||
{
|
||||
var row = (DataGridRow)generator.ContainerFromIndex(i);
|
||||
if (row != null)
|
||||
|
||||
+3
-3
@@ -3,13 +3,13 @@ using HandyControl.Data;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
public class PasswordBoxMonitor
|
||||
public class PasswordBoxAttach
|
||||
{
|
||||
/// <summary>
|
||||
/// 密码长度
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PasswordLengthProperty = DependencyProperty.RegisterAttached(
|
||||
"PasswordLength", typeof(int), typeof(PasswordBoxMonitor), new PropertyMetadata(ValueBoxes.Int0Box));
|
||||
"PasswordLength", typeof(int), typeof(PasswordBoxAttach), new PropertyMetadata(ValueBoxes.Int0Box));
|
||||
|
||||
public static void SetPasswordLength(DependencyObject element, int value) => element.SetValue(PasswordLengthProperty, value);
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace HandyControl.Controls
|
||||
/// 是否监测
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsMonitoringProperty = DependencyProperty.RegisterAttached(
|
||||
"IsMonitoring", typeof(bool), typeof(PasswordBoxMonitor), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnIsMonitoringChanged));
|
||||
"IsMonitoring", typeof(bool), typeof(PasswordBoxAttach), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnIsMonitoringChanged));
|
||||
|
||||
private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
@@ -333,7 +333,7 @@ namespace HandyControl.Controls
|
||||
}
|
||||
|
||||
var list = new List<bool>();
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
list.Add(false);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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<Col>())
|
||||
@@ -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);
|
||||
|
||||
@@ -321,7 +321,7 @@ namespace HandyControl.Controls
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
guids[i] = (Guid)ExternDllHelper.PtrToStructure((IntPtr)((long)buffer + size * i), typeof(Guid));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// 最大值
|
||||
/// </summary>
|
||||
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
|
||||
/// 最小值
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -36,11 +36,11 @@ namespace HandyControl.Controls
|
||||
|
||||
public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register(
|
||||
"DotCount", typeof(int), typeof(LoadingBase),
|
||||
new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
|
||||
public static readonly DependencyProperty DotIntervalProperty = DependencyProperty.Register(
|
||||
"DotInterval", typeof(double), typeof(LoadingBase),
|
||||
new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
new FrameworkPropertyMetadata(ValueBoxes.Double10Box, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
|
||||
public static readonly DependencyProperty DotBorderBrushProperty = DependencyProperty.Register(
|
||||
"DotBorderBrush", typeof(Brush), typeof(LoadingBase),
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace HandyControl.Controls
|
||||
StretchProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(Stretch.Uniform,
|
||||
OnPropertiesChanged));
|
||||
|
||||
StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(1.0,
|
||||
StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double1Box,
|
||||
OnPropertiesChanged));
|
||||
}
|
||||
|
||||
|
||||
@@ -85,21 +85,7 @@ namespace HandyControl.Controls
|
||||
_id = ++NextId;
|
||||
_callback = Callback;
|
||||
|
||||
Loaded += (s, e) =>
|
||||
{
|
||||
RegisterClass();
|
||||
if (Visibility == Visibility.Visible)
|
||||
{
|
||||
OnIconChanged();
|
||||
UpdateIcon(true);
|
||||
}
|
||||
|
||||
_dispatcherTimerPos = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(200)
|
||||
};
|
||||
_dispatcherTimerPos.Tick += DispatcherTimerPos_Tick;
|
||||
};
|
||||
Loaded += (s, e) => Init();
|
||||
|
||||
if (Application.Current != null) Application.Current.Exit += (s, e) => Dispose();
|
||||
}
|
||||
@@ -109,6 +95,22 @@ namespace HandyControl.Controls
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
RegisterClass();
|
||||
if (Visibility == Visibility.Visible)
|
||||
{
|
||||
OnIconChanged();
|
||||
UpdateIcon(true);
|
||||
}
|
||||
|
||||
_dispatcherTimerPos = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(200)
|
||||
};
|
||||
_dispatcherTimerPos.Tick += DispatcherTimerPos_Tick;
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
|
||||
"Text", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string)));
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace HandyControl.Controls
|
||||
/// 最大页数
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty MaxPageCountProperty = DependencyProperty.Register(
|
||||
"MaxPageCount", typeof(int), typeof(Pagination), new PropertyMetadata(1, (o, args) =>
|
||||
"MaxPageCount", typeof(int), typeof(Pagination), new PropertyMetadata(ValueBoxes.Int1Box, (o, args) =>
|
||||
{
|
||||
if (o is Pagination pagination && args.NewValue is int value)
|
||||
{
|
||||
@@ -156,7 +156,7 @@ namespace HandyControl.Controls
|
||||
/// 当前页
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register(
|
||||
"PageIndex", typeof(int), typeof(Pagination), new PropertyMetadata(1, (o, args) =>
|
||||
"PageIndex", typeof(int), typeof(Pagination), new PropertyMetadata(ValueBoxes.Int1Box, (o, args) =>
|
||||
{
|
||||
if (o is Pagination pagination && args.NewValue is int value)
|
||||
{
|
||||
@@ -312,7 +312,7 @@ namespace HandyControl.Controls
|
||||
}
|
||||
|
||||
var sub = PageIndex;
|
||||
for (int i = 0; i < MaxPageInterval - 1; i++)
|
||||
for (var i = 0; i < MaxPageInterval - 1; i++)
|
||||
{
|
||||
if (--sub > 1)
|
||||
{
|
||||
@@ -324,7 +324,7 @@ namespace HandyControl.Controls
|
||||
}
|
||||
}
|
||||
var add = PageIndex;
|
||||
for (int i = 0; i < MaxPageInterval - 1; i++)
|
||||
for (var i = 0; i < MaxPageInterval - 1; i++)
|
||||
{
|
||||
if (++add < MaxPageCount)
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace HandyControl.Controls
|
||||
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
|
||||
|
||||
var count = Items.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var item = (RadioButton)Items[i];
|
||||
item.Style = ItemStyleSelector?.SelectStyle(item, this);
|
||||
|
||||
@@ -141,8 +141,41 @@ namespace HandyControl.Controls
|
||||
}
|
||||
else if (mode == ExpandMode.ShowOne)
|
||||
{
|
||||
var sideMenuItem = ItemsHost.Children.OfType<SideMenuItem>().FirstOrDefault(item => item.IsSelected);
|
||||
ShowSelectedOne(sideMenuItem);
|
||||
SideMenuItem sideMenuItemSelected = null;
|
||||
foreach (var sideMenuItem in ItemsHost.Children.OfType<SideMenuItem>())
|
||||
{
|
||||
if (sideMenuItemSelected != null)
|
||||
{
|
||||
sideMenuItem.IsSelected = false;
|
||||
if (sideMenuItem.ItemsHost != null)
|
||||
{
|
||||
foreach (var sideMenuSubItem in sideMenuItem.ItemsHost.Children.OfType<SideMenuItem>())
|
||||
{
|
||||
sideMenuSubItem.IsSelected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sideMenuItem.IsSelected)
|
||||
{
|
||||
ShowSelectedOne(sideMenuItem);
|
||||
sideMenuItemSelected = sideMenuItem;
|
||||
|
||||
if (sideMenuItem.ItemsHost != null)
|
||||
{
|
||||
foreach (var sideMenuSubItem in sideMenuItem.ItemsHost.Children.OfType<SideMenuItem>())
|
||||
{
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
sideMenuSubItem.IsSelected = false;
|
||||
}
|
||||
else if (sideMenuSubItem.IsSelected)
|
||||
{
|
||||
_selectedItem = sideMenuSubItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -1,7 +1,6 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
public class CompareSlider : Slider
|
||||
File renamed without changes.
@@ -0,0 +1,653 @@
|
||||
// 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);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 (var 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<TwoWayRange>(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<TwoWayRange>), typeof(TwoWayRangeBase));
|
||||
|
||||
public event RoutedPropertyChangedEventHandler<TwoWayRange> ValueChanged
|
||||
{
|
||||
add => AddHandler(ValueChangedEvent, value);
|
||||
remove => RemoveHandler(ValueChangedEvent, value);
|
||||
}
|
||||
}
|
||||
|
||||
public struct TwoWayRange
|
||||
{
|
||||
public double ValueStart { get; set; }
|
||||
|
||||
public double ValueEnd { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace HandyControl.Controls
|
||||
var count = Items.Count;
|
||||
if (count <= 0) return;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepBarItem)
|
||||
{
|
||||
@@ -48,7 +48,7 @@ namespace HandyControl.Controls
|
||||
|
||||
if (StepIndex < count)
|
||||
{
|
||||
for (int i = 0; i < StepIndex; i++)
|
||||
for (var i = 0; i < StepIndex; i++)
|
||||
{
|
||||
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepBarItem)
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace HandyControl.Controls
|
||||
private void CreateItemsSource(ItemsControl selector, int count)
|
||||
{
|
||||
var list = new List<string>();
|
||||
for (int i = 0; i < count; i++)
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
list.Add(i.ToString("#00"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Media;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
@@ -13,6 +15,7 @@ namespace HandyControl.Controls
|
||||
/// <summary>
|
||||
/// 消息框
|
||||
/// </summary>
|
||||
[SuppressMessage("ReSharper", "RedundantDelegateCreation")]
|
||||
public sealed class MessageBox : Window
|
||||
{
|
||||
private MessageBoxResult _messageBoxResult = MessageBoxResult.Cancel;
|
||||
@@ -30,7 +33,7 @@ namespace HandyControl.Controls
|
||||
|
||||
public string Message
|
||||
{
|
||||
get => (string) GetValue(MessageProperty);
|
||||
get => (string)GetValue(MessageProperty);
|
||||
set => SetValue(MessageProperty, value);
|
||||
}
|
||||
|
||||
@@ -39,7 +42,7 @@ namespace HandyControl.Controls
|
||||
|
||||
public Geometry Image
|
||||
{
|
||||
get => (Geometry) GetValue(ImageProperty);
|
||||
get => (Geometry)GetValue(ImageProperty);
|
||||
set => SetValue(ImageProperty, value);
|
||||
}
|
||||
|
||||
@@ -48,7 +51,7 @@ namespace HandyControl.Controls
|
||||
|
||||
public Brush ImageBrush
|
||||
{
|
||||
get => (Brush) GetValue(ImageBrushProperty);
|
||||
get => (Brush)GetValue(ImageBrushProperty);
|
||||
set => SetValue(ImageBrushProperty, value);
|
||||
}
|
||||
|
||||
@@ -57,7 +60,7 @@ namespace HandyControl.Controls
|
||||
|
||||
public bool ShowImage
|
||||
{
|
||||
get => (bool) GetValue(ShowImageProperty);
|
||||
get => (bool)GetValue(ShowImageProperty);
|
||||
set => SetValue(ShowImageProperty, value);
|
||||
}
|
||||
|
||||
@@ -138,7 +141,6 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Success(string messageBoxText, string caption = null)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);
|
||||
@@ -146,19 +148,9 @@ namespace HandyControl.Controls
|
||||
messageBox.ShowImage = true;
|
||||
messageBox.Image = ResourceHelper.GetResource<Geometry>(ResourceToken.SuccessGeometry);
|
||||
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(ResourceToken.SuccessBrush);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
messageBox.ShowImage = true;
|
||||
messageBox.Image = ResourceHelper.GetResource<Geometry>(ResourceToken.SuccessGeometry);
|
||||
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(ResourceToken.SuccessBrush);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -171,23 +163,14 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Info(string messageBoxText, string caption = null)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
SetImage(messageBox, MessageBoxImage.Information);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
SetImage(messageBox, MessageBoxImage.Information);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -200,23 +183,14 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Warning(string messageBoxText, string caption = null)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
SetImage(messageBox, MessageBoxImage.Warning);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
SetImage(messageBox, MessageBoxImage.Warning);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -229,23 +203,14 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Error(string messageBoxText, string caption = null)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
SetImage(messageBox, MessageBoxImage.Error);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
SetImage(messageBox, MessageBoxImage.Error);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -258,7 +223,6 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Fatal(string messageBoxText, string caption = null)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);
|
||||
@@ -266,19 +230,9 @@ namespace HandyControl.Controls
|
||||
messageBox.ShowImage = true;
|
||||
messageBox.Image = ResourceHelper.GetResource<Geometry>(ResourceToken.FatalGeometry);
|
||||
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(ResourceToken.PrimaryTextBrush);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OK);
|
||||
messageBox.ShowImage = true;
|
||||
messageBox.Image = ResourceHelper.GetResource<Geometry>(ResourceToken.FatalGeometry);
|
||||
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(ResourceToken.PrimaryTextBrush);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -291,23 +245,14 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Ask(string messageBoxText, string caption = null)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OKCancel);
|
||||
SetImage(messageBox, MessageBoxImage.Question);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
|
||||
SetButtonStatus(messageBox, MessageBoxButton.OKCancel);
|
||||
SetImage(messageBox, MessageBoxImage.Question);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -320,7 +265,6 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Show(MessageBoxInfo info)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, info.MessageBoxText, info.Caption, info.Button, MessageBoxImage.None, info.DefaultResult);
|
||||
@@ -333,26 +277,13 @@ namespace HandyControl.Controls
|
||||
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(info.IconBrushKey);
|
||||
}
|
||||
|
||||
messageBox.Style = info.Style;
|
||||
if (info.Style != null)
|
||||
{
|
||||
messageBox.Style = info.Style;
|
||||
}
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(null, info.MessageBoxText, info.Caption, info.Button, MessageBoxImage.None, info.DefaultResult);
|
||||
SetButtonStatus(messageBox, info.Button);
|
||||
|
||||
if (!string.IsNullOrEmpty(info.IconKey))
|
||||
{
|
||||
messageBox.ShowImage = true;
|
||||
messageBox.Image = ResourceHelper.GetResource<Geometry>(info.IconKey);
|
||||
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(info.IconBrushKey);
|
||||
}
|
||||
|
||||
messageBox.Style = info.Style;
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -384,23 +315,14 @@ namespace HandyControl.Controls
|
||||
public static MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None)
|
||||
{
|
||||
MessageBox messageBox = null;
|
||||
#if netle40
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(owner, messageBoxText, caption, button, icon, defaultResult);
|
||||
SetButtonStatus(messageBox, button);
|
||||
SetImage(messageBox, icon);
|
||||
SystemSounds.Asterisk.Play();
|
||||
messageBox.ShowDialog();
|
||||
}));
|
||||
#else
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
messageBox = CreateMessageBox(owner, messageBoxText, caption, button, icon, defaultResult);
|
||||
SetButtonStatus(messageBox, button);
|
||||
SetImage(messageBox, icon);
|
||||
messageBox.ShowDialog();
|
||||
});
|
||||
#endif
|
||||
|
||||
return messageBox._messageBoxResult;
|
||||
}
|
||||
@@ -525,4 +447,4 @@ namespace HandyControl.Controls
|
||||
|| value == MessageBoxResult.Yes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ using HandyControl.Tools.Extension;
|
||||
using Microsoft.Windows.Shell;
|
||||
#else
|
||||
using System.Windows.Shell;
|
||||
|
||||
#endif
|
||||
|
||||
namespace HandyControl.Controls
|
||||
|
||||
Reference in new issue
Block a user