From c1ed2b3b280b4341139796b2c7ec558f888293fa Mon Sep 17 00:00:00 2001 From: dzhelnin Date: Tue, 20 Mar 2018 15:14:53 +0300 Subject: [PATCH] Added concrete implementations for various numeric types --- .../NumericUpDown/AllowedSpecialValues.cs | 15 +++ .../NumericUpDown/ByteUpDown.cs | 24 ++++ .../NumericUpDown/CommonNumericUpDown.cs | 2 +- .../NumericUpDown/DecimalUpDown.cs | 24 ++++ .../NumericUpDown/DoubleUpDown.cs | 109 ++++++++++++++++++ .../NumericUpDown/IntegerUpDown.cs | 24 ++++ .../NumericUpDown/LongUpDown.cs | 24 ++++ .../NumericUpDown/ShortUpDown.cs | 24 ++++ .../NumericUpDown/SingleUpDown.cs | 106 +++++++++++++++++ 9 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Controls/NumericUpDown/AllowedSpecialValues.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/ByteUpDown.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/DecimalUpDown.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/DoubleUpDown.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/IntegerUpDown.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/LongUpDown.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/ShortUpDown.cs create mode 100644 src/Avalonia.Controls/NumericUpDown/SingleUpDown.cs diff --git a/src/Avalonia.Controls/NumericUpDown/AllowedSpecialValues.cs b/src/Avalonia.Controls/NumericUpDown/AllowedSpecialValues.cs new file mode 100644 index 0000000000..a4ec5ecdaf --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/AllowedSpecialValues.cs @@ -0,0 +1,15 @@ +using System; + +namespace Avalonia.Controls +{ + [Flags] + public enum AllowedSpecialValues + { + None = 0, + NaN = 1, + PositiveInfinity = 2, + NegativeInfinity = 4, + AnyInfinity = PositiveInfinity | NegativeInfinity, + Any = NaN | AnyInfinity + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/ByteUpDown.cs b/src/Avalonia.Controls/NumericUpDown/ByteUpDown.cs new file mode 100644 index 0000000000..835abae773 --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/ByteUpDown.cs @@ -0,0 +1,24 @@ +namespace Avalonia.Controls +{ + /// + public class ByteUpDown : CommonNumericUpDown + { + /// + /// Initializes static members of the class. + /// + static ByteUpDown() => UpdateMetadata(typeof(ByteUpDown), 1, byte.MinValue, byte.MaxValue); + + /// + /// Initializes new instance of the class. + /// + public ByteUpDown() : base(byte.TryParse, decimal.ToByte, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + protected override byte IncrementValue(byte value, byte increment) => (byte)(value + increment); + + /// + protected override byte DecrementValue(byte value, byte increment) => (byte)(value - increment); + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/CommonNumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/CommonNumericUpDown.cs index 1ce2508b4a..eed792d9dd 100644 --- a/src/Avalonia.Controls/NumericUpDown/CommonNumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/CommonNumericUpDown.cs @@ -6,7 +6,7 @@ using System.Linq; namespace Avalonia.Controls { /// - /// Base class for controls that represents a TextBox with button spinners that allow incrementing and decrementing numeric values. + /// Base class for controls that represents a TextBox with button spinners that allow incrementing and decrementing nullable numeric values. /// public abstract class CommonNumericUpDown : NumericUpDown where T : struct, IFormattable, IComparable { diff --git a/src/Avalonia.Controls/NumericUpDown/DecimalUpDown.cs b/src/Avalonia.Controls/NumericUpDown/DecimalUpDown.cs new file mode 100644 index 0000000000..10cfa537d7 --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/DecimalUpDown.cs @@ -0,0 +1,24 @@ +namespace Avalonia.Controls +{ + /// + public class DecimalUpDown : CommonNumericUpDown + { + /// + /// Initializes static members of the class. + /// + static DecimalUpDown() => UpdateMetadata(typeof(DecimalUpDown), 1m, decimal.MinValue, decimal.MaxValue); + + /// + /// Initializes new instance of the class. + /// + public DecimalUpDown() : base(decimal.TryParse, d => d, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + protected override decimal IncrementValue(decimal value, decimal increment) => value + increment; + + /// + protected override decimal DecrementValue(decimal value, decimal increment) => value - increment; + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/DoubleUpDown.cs b/src/Avalonia.Controls/NumericUpDown/DoubleUpDown.cs new file mode 100644 index 0000000000..edee1247fb --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/DoubleUpDown.cs @@ -0,0 +1,109 @@ +using System; + +namespace Avalonia.Controls +{ + /// + public class DoubleUpDown : CommonNumericUpDown + { + /// + /// Defines the property. + /// + public static readonly DirectProperty AllowInputSpecialValuesProperty = + AvaloniaProperty.RegisterDirect(nameof(AllowInputSpecialValues), + updown => updown.AllowInputSpecialValues, (updown, v) => updown.AllowInputSpecialValues = v); + + private AllowedSpecialValues _allowInputSpecialValues; + + /// + /// Initializes static members of the class. + /// + static DoubleUpDown() => UpdateMetadata(typeof(DoubleUpDown), 1d, double.NegativeInfinity, double.PositiveInfinity); + + /// + /// Initializes new instance of the class. + /// + public DoubleUpDown() : base(double.TryParse, decimal.ToDouble, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + /// Gets or sets a value representing the special values the user is allowed to input, such as "Infinity", "-Infinity" and "NaN" values. + /// + public AllowedSpecialValues AllowInputSpecialValues + { + get { return _allowInputSpecialValues; } + set { SetAndRaise(AllowInputSpecialValuesProperty, ref _allowInputSpecialValues, value); } + } + + /// + protected override double IncrementValue(double value, double increment) => value + increment; + + /// + protected override double DecrementValue(double value, double increment) => value - increment; + + /// + protected override double? OnCoerceIncrement(double? baseValue) + { + if (baseValue.HasValue && double.IsNaN(baseValue.Value)) + { + throw new ArgumentException("NaN is invalid for Increment."); + } + return base.OnCoerceIncrement(baseValue); + } + + /// + protected override double? OnCoerceMaximum(double? baseValue) + { + if (baseValue.HasValue && double.IsNaN(baseValue.Value)) + { + throw new ArgumentException("NaN is invalid for Maximum."); + } + return base.OnCoerceMaximum(baseValue); + } + + /// + protected override double? OnCoerceMinimum(double? baseValue) + { + if (baseValue.HasValue && double.IsNaN(baseValue.Value)) + { + throw new ArgumentException("NaN is invalid for Minimum."); + } + return base.OnCoerceMinimum(baseValue); + } + + /// + protected override void SetValidSpinDirection() + { + if (Value.HasValue && double.IsInfinity(Value.Value) && (Spinner != null)) + { + Spinner.ValidSpinDirection = ValidSpinDirections.None; + } + else + { + base.SetValidSpinDirection(); + } + } + + /// + protected override double? ConvertTextToValue(string text) + { + var result = base.ConvertTextToValue(text); + if (result != null) + { + if (double.IsNaN(result.Value)) + { + TestInputSpecialValue(AllowInputSpecialValues, AllowedSpecialValues.NaN); + } + else if (double.IsPositiveInfinity(result.Value)) + { + TestInputSpecialValue(AllowInputSpecialValues, AllowedSpecialValues.PositiveInfinity); + } + else if (double.IsNegativeInfinity(result.Value)) + { + TestInputSpecialValue(AllowInputSpecialValues, AllowedSpecialValues.NegativeInfinity); + } + } + return result; + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/IntegerUpDown.cs b/src/Avalonia.Controls/NumericUpDown/IntegerUpDown.cs new file mode 100644 index 0000000000..7f9e05762c --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/IntegerUpDown.cs @@ -0,0 +1,24 @@ +namespace Avalonia.Controls +{ + /// + public class IntegerUpDown : CommonNumericUpDown + { + /// + /// Initializes static members of the class. + /// + static IntegerUpDown() => UpdateMetadata(typeof(IntegerUpDown), 1, int.MinValue, int.MaxValue); + + /// + /// Initializes new instance of the class. + /// + public IntegerUpDown() : base(int.TryParse, decimal.ToInt32, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + protected override int IncrementValue(int value, int increment) => value + increment; + + /// + protected override int DecrementValue(int value, int increment) => value - increment; + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/LongUpDown.cs b/src/Avalonia.Controls/NumericUpDown/LongUpDown.cs new file mode 100644 index 0000000000..d116df0ad2 --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/LongUpDown.cs @@ -0,0 +1,24 @@ +namespace Avalonia.Controls +{ + /// + public class LongUpDown : CommonNumericUpDown + { + /// + /// Initializes static members of the class. + /// + static LongUpDown() => UpdateMetadata(typeof(LongUpDown), 1L, long.MinValue, long.MaxValue); + + /// + /// Initializes new instance of the class. + /// + public LongUpDown() : base(long.TryParse, decimal.ToInt64, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + protected override long IncrementValue(long value, long increment) => value + increment; + + /// + protected override long DecrementValue(long value, long increment) => value - increment; + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/ShortUpDown.cs b/src/Avalonia.Controls/NumericUpDown/ShortUpDown.cs new file mode 100644 index 0000000000..cdba5b9b64 --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/ShortUpDown.cs @@ -0,0 +1,24 @@ +namespace Avalonia.Controls +{ + /// + public class ShortUpDown : CommonNumericUpDown + { + /// + /// Initializes static members of the class. + /// + static ShortUpDown() => UpdateMetadata(typeof(ShortUpDown), 1, short.MinValue, short.MaxValue); + + /// + /// Initializes new instance of the class. + /// + public ShortUpDown() : base(short.TryParse, decimal.ToInt16, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + protected override short IncrementValue(short value, short increment) => (short)(value + increment); + + /// + protected override short DecrementValue(short value, short increment) => (short)(value - increment); + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/NumericUpDown/SingleUpDown.cs b/src/Avalonia.Controls/NumericUpDown/SingleUpDown.cs new file mode 100644 index 0000000000..cc3da078bf --- /dev/null +++ b/src/Avalonia.Controls/NumericUpDown/SingleUpDown.cs @@ -0,0 +1,106 @@ +using System; + +namespace Avalonia.Controls +{ + /// + public class SingleUpDown : CommonNumericUpDown + { + /// + /// Defines the property. + /// + public static readonly DirectProperty AllowInputSpecialValuesProperty = + AvaloniaProperty.RegisterDirect(nameof(AllowInputSpecialValues), + updown => updown.AllowInputSpecialValues, (updown, v) => updown.AllowInputSpecialValues = v); + + private AllowedSpecialValues _allowInputSpecialValues; + + /// + /// Initializes static members of the class. + /// + static SingleUpDown() => UpdateMetadata(typeof(SingleUpDown), 1f, float.NegativeInfinity, float.PositiveInfinity); + + /// + /// Initializes new instance of the class. + /// + public SingleUpDown() : base(float.TryParse, decimal.ToSingle, (v1, v2) => v1 < v2, (v1, v2) => v1 > v2) + { + } + + /// + /// Gets or sets a value representing the special values the user is allowed to input, such as "Infinity", "-Infinity" and "NaN" values. + /// + public AllowedSpecialValues AllowInputSpecialValues + { + get { return _allowInputSpecialValues; } + set { SetAndRaise(AllowInputSpecialValuesProperty, ref _allowInputSpecialValues, value); } + } + + /// + protected override float? OnCoerceIncrement(float? baseValue) + { + if (baseValue.HasValue && float.IsNaN(baseValue.Value)) + throw new ArgumentException("NaN is invalid for Increment."); + + return base.OnCoerceIncrement(baseValue); + } + + /// + protected override float? OnCoerceMaximum(float? baseValue) + { + if (baseValue.HasValue && float.IsNaN(baseValue.Value)) + throw new ArgumentException("NaN is invalid for Maximum."); + + return base.OnCoerceMaximum(baseValue); + } + + /// + protected override float? OnCoerceMinimum(float? baseValue) + { + if (baseValue.HasValue && float.IsNaN(baseValue.Value)) + throw new ArgumentException("NaN is invalid for Minimum."); + + return base.OnCoerceMinimum(baseValue); + } + + /// + protected override float IncrementValue(float value, float increment) => value + increment; + + /// + protected override float DecrementValue(float value, float increment) => value - increment; + + /// + protected override void SetValidSpinDirection() + { + if (Value.HasValue && float.IsInfinity(Value.Value) && (Spinner != null)) + { + Spinner.ValidSpinDirection = ValidSpinDirections.None; + } + else + { + base.SetValidSpinDirection(); + } + } + + /// + protected override float? ConvertTextToValue(string text) + { + var result = base.ConvertTextToValue(text); + if (result != null) + { + if (float.IsNaN(result.Value)) + { + TestInputSpecialValue(AllowInputSpecialValues, AllowedSpecialValues.NaN); + } + else if (float.IsPositiveInfinity(result.Value)) + { + TestInputSpecialValue(AllowInputSpecialValues, AllowedSpecialValues.PositiveInfinity); + } + else if (float.IsNegativeInfinity(result.Value)) + { + TestInputSpecialValue(AllowInputSpecialValues, AllowedSpecialValues.NegativeInfinity); + } + } + return result; + } + } +} \ No newline at end of file