diff --git a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml index 4d430b511..2cfcb6c63 100644 --- a/src/Wpf.Ui.Demo.Simple/MainWindow.xaml +++ b/src/Wpf.Ui.Demo.Simple/MainWindow.xaml @@ -86,12 +86,9 @@ Grid.Row="0" Icon="pack://application:,,,/Assets/applicationIcon-256.png" /> - + diff --git a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs index 208954d9c..43b2d34b4 100644 --- a/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs +++ b/src/Wpf.Ui/Controls/NumberBox/NumberBox.cs @@ -2,9 +2,9 @@ // If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT. // Copyright (C) Leszek Pomianowski and WPF UI Contributors. // All Rights Reserved. - // This Source Code is partially based on the source code provided by the .NET Foundation. +using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; @@ -19,14 +19,12 @@ namespace Wpf.Ui.Controls; /// /// Represents a control that can be used to display and edit numbers. /// -//[ToolboxItem(true)] -//[ToolboxBitmap(typeof(NumberBox), "NumberBox.bmp")] -public class NumberBox : Wpf.Ui.Controls.TextBox +// [ToolboxItem(true)] +// [ToolboxBitmap(typeof(NumberBox), "NumberBox.bmp")] +public class NumberBox : TextBox { private bool _valueUpdating; - private bool _textUpdating; - /// /// Property for . /// @@ -37,7 +35,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - OnValuePropertyChanged, + OnValueChanged, null, false, UpdateSourceTrigger.LostFocus @@ -61,7 +59,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox nameof(SmallChange), typeof(double), typeof(NumberBox), - new PropertyMetadata(1.0d) + new PropertyMetadata(1.0) ); /// @@ -71,7 +69,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox nameof(LargeChange), typeof(double), typeof(NumberBox), - new PropertyMetadata(10.0d) + new PropertyMetadata(10.0) ); /// @@ -81,7 +79,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox nameof(Maximum), typeof(double), typeof(NumberBox), - new PropertyMetadata(Double.MaxValue) + new PropertyMetadata(double.MaxValue) ); /// @@ -91,7 +89,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox nameof(Minimum), typeof(double), typeof(NumberBox), - new PropertyMetadata(Double.MinValue) + new PropertyMetadata(double.MinValue) ); /// @@ -124,6 +122,8 @@ public class NumberBox : Wpf.Ui.Controls.TextBox new PropertyMetadata(NumberBoxValidationMode.InvalidInputOverwritten) ); + private static readonly ValidateNumberFormatter DefaultNumberFormatter = GetRegionalSettingsAwareDecimalFormatter(); + /// /// Property for . /// @@ -131,7 +131,7 @@ public class NumberBox : Wpf.Ui.Controls.TextBox nameof(NumberFormatter), typeof(INumberFormatter), typeof(NumberBox), - new PropertyMetadata(null, OnNumberFormatterPropertyChanged) + new PropertyMetadata(DefaultNumberFormatter, OnNumberFormatterChanged) ); /// @@ -144,6 +144,37 @@ public class NumberBox : Wpf.Ui.Controls.TextBox typeof(NumberBox) ); + private static ValidateNumberFormatter GetRegionalSettingsAwareDecimalFormatter() + { + return new ValidateNumberFormatter(); + } + + private static void OnNumberFormatterChanged( + DependencyObject d, + DependencyPropertyChangedEventArgs e + ) + { + if (e.NewValue is INumberParser) + { + return; + } + + throw new ArgumentException( + $"{nameof(NumberFormatter)} must implement {typeof(INumberParser)}", + nameof(NumberFormatter) + ); + } + + private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is not NumberBox numberBox) + { + return; + } + + numberBox.OnValueChanged(d, (double?)e.OldValue); + } + /// /// Gets or sets the numeric value of a . /// @@ -199,7 +230,7 @@ public double Minimum } /// - /// Gets or sets whether the control will accept and evaluate a basic formulaic expression entered as input. + /// Gets or sets a value indicating whether the control will accept and evaluate a basic formulaic expression entered as input. /// public bool AcceptsExpression { @@ -250,15 +281,6 @@ static NumberBox() MinLinesProperty.OverrideMetadata(typeof(NumberBox), new FrameworkPropertyMetadata(1)); } - /// - public NumberBox() - : base() - { - NumberFormatter ??= GetRegionalSettingsAwareDecimalFormatter(); - - DataObject.AddPastingHandler(this, OnClipboardPaste); - } - /// protected override void OnKeyUp(KeyEventArgs e) { @@ -286,7 +308,8 @@ protected override void OnKeyUp(KeyEventArgs e) case Key.Enter: if (TextWrapping != TextWrapping.Wrap) { - ValidateInput(); + UpdateValueFromText(); + ValidateValue(Value); MoveCaretToTextEnd(); } @@ -324,49 +347,59 @@ protected override void OnTemplateButtonClick(string? parameter) Focus(); } - /// - protected override void OnLostFocus(RoutedEventArgs e) + private void UpdateValueFromText() { - base.OnLostFocus(e); - - ValidateInput(); + SetCurrentValue(ValueProperty, GetParser().ParseDouble(Text.Trim())); } - /// - //protected override void OnTextChanged(System.Windows.Controls.TextChangedEventArgs e) - //{ - // base.OnTextChanged(e); - - // //if (new string[] { ",", ".", " " }.Any(s => Text.EndsWith(s))) - // // return; - - // //if (!_textUpdating) - // // UpdateValueToText(); - //} + private INumberParser GetParser() + { + return (NumberFormatter as INumberParser) ?? DefaultNumberFormatter; + } /// protected override void OnTemplateChanged( - System.Windows.Controls.ControlTemplate oldTemplate, - System.Windows.Controls.ControlTemplate newTemplate + ControlTemplate oldTemplate, + ControlTemplate newTemplate ) { base.OnTemplateChanged(oldTemplate, newTemplate); // If Text has been set, but Value hasn't, update Value based on Text. - if (String.IsNullOrEmpty(Text) && Value != null) + if (string.IsNullOrEmpty(Text) && Value != null) { - UpdateValueToText(); + SetCurrentValue(ValueProperty, null); } else { - UpdateTextToValue(); + UpdateValueFromText(); + ValidateValue(Value); } } + protected override void OnLostFocus(RoutedEventArgs e) + { + UpdateValueFromText(); + ValidateValue(Value); + base.OnLostFocus(e); + } + /// /// Is called when in this changes. /// protected virtual void OnValueChanged(DependencyObject d, double? oldValue) + { + double? newValue = Value; + + if (Equals(newValue, oldValue)) + { + return; + } + + ValidateValue(newValue); + } + + private void ValidateValue(double? newValue) { if (_valueUpdating) { @@ -375,40 +408,29 @@ protected virtual void OnValueChanged(DependencyObject d, double? oldValue) _valueUpdating = true; - var newValue = Value; - if (newValue > Maximum) { - SetCurrentValue(ValueProperty, Maximum); + newValue = Maximum; } - - if (newValue < Minimum) + else if (newValue < Minimum) { - SetCurrentValue(ValueProperty, Minimum); + newValue = Minimum; } - if (!Equals(newValue, oldValue)) + SetCurrentValue(ValueProperty, newValue); + RaiseEvent(new(ValueChangedEvent)); + + // Correct the text from value + if (Value is null && Text.Length > 0) { - RaiseEvent(new RoutedEventArgs(ValueChangedEvent)); + SetCurrentValue(TextProperty, string.Empty); } - - UpdateTextToValue(); - - _valueUpdating = false; - } - - /// - /// Is called when something is pasted in this . - /// - protected virtual void OnClipboardPaste(object sender, DataObjectPastingEventArgs e) - { - // TODO: Fix clipboard - if (sender is not NumberBox) + else if (GetParser().ParseDouble(Text.Trim()) != Value) { - return; + SetCurrentValue(TextProperty, NumberFormatter.FormatDouble(Value)); } - ValidateInput(); + _valueUpdating = false; } private void StepValue(double? change) @@ -420,10 +442,7 @@ private void StepValue(double? change) ); #endif - // Before adjusting the value, validate the contents of the textbox so we don't override it. - ValidateInput(); - - var newValue = Value ?? 0; + double newValue = Value ?? 0; if (change is not null) { @@ -435,97 +454,8 @@ private void StepValue(double? change) MoveCaretToTextEnd(); } - private void UpdateTextToValue() - { - _textUpdating = true; - - // text = value - var newText = String.Empty; - - if (Value is not null) - { - newText = NumberFormatter.FormatDouble(Math.Round((double)Value, MaxDecimalPlaces)); - } - - SetCurrentValue(TextProperty, newText); - - _textUpdating = false; - } - - private void UpdateValueToText() - { - ValidateInput(); - } - - private void ValidateInput() - { - var text = Text.Trim(); - - if (String.IsNullOrEmpty(text)) - { - SetCurrentValue(ValueProperty, null); - - return; - } - - var numberParser = NumberFormatter as INumberParser; - var value = numberParser!.ParseDouble(text); - - if (value is null || Equals(Value, value)) - { - UpdateTextToValue(); - - return; - } - - if (value > Maximum) - { - value = Maximum; - } - - if (value < Minimum) - { - value = Minimum; - } - - SetCurrentValue(ValueProperty, value); - - UpdateTextToValue(); - } - private void MoveCaretToTextEnd() { CaretIndex = Text.Length; } - - private INumberFormatter GetRegionalSettingsAwareDecimalFormatter() - { - return new ValidateNumberFormatter(); - } - - private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not NumberBox numberBox) - { - return; - } - - numberBox.OnValueChanged(d, (double?)e.OldValue); - } - - private static void OnNumberFormatterPropertyChanged( - DependencyObject d, - DependencyPropertyChangedEventArgs e - ) - { - if (e.NewValue is INumberParser) - { - return; - } - - throw new ArgumentException( - $"{nameof(NumberFormatter)} must implement {typeof(INumberParser)}", - nameof(NumberFormatter) - ); - } } diff --git a/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs b/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs index 65a302a44..6a7063cf4 100644 --- a/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs +++ b/src/Wpf.Ui/Controls/NumberBox/ValidateNumberFormatter.cs @@ -34,25 +34,19 @@ public string FormatUInt(uint? value) /// public double? ParseDouble(string? value) { - Double.TryParse(value, out double d); - - return d; + return Double.TryParse(value, out var d) ? d : null; } /// public int? ParseInt(string? value) { - Int32.TryParse(value, out int i); - - return i; + return Int32.TryParse(value, out var d) ? d : null; } /// public uint? ParseUInt(string? value) { - UInt32.TryParse(value, out uint ui); - - return ui; + return UInt32.TryParse(value, out var d) ? d : null; } private static string GetFormatSpecifier()