diff --git a/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/TextExamples.xaml b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/TextExamples.xaml index cac99344a6..f73cee2ac2 100644 --- a/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/TextExamples.xaml +++ b/src/MahApps.Metro.Samples/MahApps.Metro.Demo/ExampleViews/TextExamples.xaml @@ -324,316 +324,207 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + 2 + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + this.Set(ref this.currentCulture, value); } - private double? numericUpDownValue = null; + private double numericUpDownValue = default; - public double? NumericUpDownValue + public double NumericUpDownValue { get => this.numericUpDownValue; set => this.Set(ref this.numericUpDownValue, value); } + private double? nullableNumericUpDownValue = null; + + public double? NullableNumericUpDownValue + { + get => this.nullableNumericUpDownValue; + set => this.Set(ref this.nullableNumericUpDownValue, value); + } + + public ICommand EndOfScrollReachedCmdWithParameter { get; } public int? IntegerGreater10Property diff --git a/src/MahApps.Metro/Controls/NumericUpDown.cs b/src/MahApps.Metro/Controls/NumericUpDown.cs index 462d6273b2..0b62e43534 100644 --- a/src/MahApps.Metro/Controls/NumericUpDown.cs +++ b/src/MahApps.Metro/Controls/NumericUpDown.cs @@ -378,32 +378,32 @@ private static void OnValuePropertyChanged(DependencyObject dependencyObject, De } [MustUseReturnValue] - private static object? CoerceValue(DependencyObject d, object? value) + private static object? CoerceValue(DependencyObject d, object? baseValue) { - if (value is null) + var numericUpDown = (NumericUpDown)d; + if (baseValue is null) { - return null; + return numericUpDown.DefaultValue; } - var numericUpDown = (NumericUpDown)d; - double val = ((double?)value).Value; + var value = ((double?)baseValue).Value; if (!numericUpDown.NumericInputMode.HasFlag(NumericInput.Decimal)) { - val = Math.Truncate(val); + value = Math.Truncate(value); } - if (val < numericUpDown.Minimum) + if (value < numericUpDown.Minimum) { return numericUpDown.Minimum; } - if (val > numericUpDown.Maximum) + if (value > numericUpDown.Maximum) { return numericUpDown.Maximum; } - return val; + return value; } /// @@ -418,6 +418,56 @@ private static void OnValuePropertyChanged(DependencyObject dependencyObject, De set => this.SetValue(ValueProperty, value); } + /// Identifies the dependency property. + public static readonly DependencyProperty DefaultValueProperty + = DependencyProperty.Register(nameof(DefaultValue), + typeof(double?), + typeof(NumericUpDown), + new PropertyMetadata(null, OnDefaultValuePropertyChanged, CoerceDefaultValue)); + + private static void OnDefaultValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var numericUpDown = (NumericUpDown)d; + + if (!numericUpDown.Value.HasValue && numericUpDown.DefaultValue.HasValue) + { + numericUpDown.SetValueTo(numericUpDown.DefaultValue.Value); + } + } + + [MustUseReturnValue] + private static object? CoerceDefaultValue(DependencyObject d, object? baseValue) + { + if (baseValue is double val) + { + var minimum = ((NumericUpDown)d).Minimum; + var maximum = ((NumericUpDown)d).Maximum; + + if (val < minimum) + { + return minimum; + } + else if (val > maximum) + { + return maximum; + } + } + + return baseValue; + } + + /// + /// Gets or sets the default value of the NumericUpDown which will be used if the is . + /// + [Bindable(true)] + [Category("Common")] + [DefaultValue(null)] + public double? DefaultValue + { + get => (double?)this.GetValue(DefaultValueProperty); + set => this.SetValue(DefaultValueProperty, value); + } + /// Identifies the dependency property. public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(nameof(Minimum), @@ -431,6 +481,7 @@ private static void OnMinimumPropertyChanged(DependencyObject d, DependencyPrope numericUpDown.CoerceValue(MaximumProperty); numericUpDown.CoerceValue(ValueProperty); + numericUpDown.CoerceValue(DefaultValueProperty); numericUpDown.OnMinimumChanged((double)e.OldValue, (double)e.NewValue); numericUpDown.EnableDisableUpDown(); } @@ -459,6 +510,7 @@ private static void OnMaximumPropertyChanged(DependencyObject d, DependencyPrope var numericUpDown = (NumericUpDown)d; numericUpDown.CoerceValue(ValueProperty); + numericUpDown.CoerceValue(DefaultValueProperty); numericUpDown.OnMaximumChanged((double)e.OldValue, (double)e.NewValue); numericUpDown.EnableDisableUpDown(); } @@ -1432,6 +1484,10 @@ private void ChangeValueFromTextInput(TextBox? textBox) convertedValue = FormattedValue(convertedValue, this.StringFormat, this.SpecificCultureInfo); this.SetValueTo(convertedValue); } + else if (this.DefaultValue.HasValue) + { + this.SetValueTo(this.DefaultValue.Value); + } } this.OnValueChanged(this.Value, this.Value); @@ -1446,7 +1502,18 @@ private void OnTextChanged(object sender, TextChangedEventArgs e) if (string.IsNullOrEmpty(((TextBox)sender).Text)) { - this.Value = null; + if (this.DefaultValue.HasValue) + { + this.SetValueTo(this.DefaultValue.Value); + if (!this.manualChange) + { + this.InternalSetText(this.DefaultValue.Value); + } + } + else + { + this.SetCurrentValue(ValueProperty, null); + } } else if (this.manualChange || e.UndoAction == UndoAction.Undo || e.UndoAction == UndoAction.Redo) { diff --git a/src/Mahapps.Metro.Tests/Tests/NumericUpDownTests.cs b/src/Mahapps.Metro.Tests/Tests/NumericUpDownTests.cs index 59836ca1ae..1f0315d9c4 100644 --- a/src/Mahapps.Metro.Tests/Tests/NumericUpDownTests.cs +++ b/src/Mahapps.Metro.Tests/Tests/NumericUpDownTests.cs @@ -331,5 +331,45 @@ private static void SetText(TextBox theTextBox, string theText) theTextBox.RaiseEvent(textCompositionEventArgs); theTextBox.RaiseEvent(new RoutedEventArgs(UIElement.LostFocusEvent)); } + + [Fact] + public async Task ShouldSetDefaultValue() + { + // Prepare + await this.fixture.PrepareForTestAsync().ConfigureAwait(true); + await TestHost.SwitchToAppThread(); + + var nud = this.fixture.Window.TheNUD; + nud.Minimum = 0; + nud.Maximum = 10; + + // 1. Test: The default value must be set here. Let's check this. + + nud.DefaultValue = 1; + nud.Value = null; + + Assert.Equal(nud.Value, nud.DefaultValue); + + // 2. Test: There is no default value, so we should be able to set it to null + + nud.DefaultValue = null; + nud.Value = null; + + Assert.Equal(nud.Value, nud.DefaultValue); + + // 3. Test: We set the Default Value greater than the Maximum. It should be corrected by the control + nud.DefaultValue = 100; + nud.Value = null; + + Assert.Equal(nud.Maximum, nud.DefaultValue); + Assert.Equal(nud.Maximum, nud.Value); + + // 4. Test: We set the Default Value lower than the Minimum. It should be corrected by the control + nud.DefaultValue = -100; + nud.Value = null; + + Assert.Equal(nud.Minimum, nud.DefaultValue); + Assert.Equal(nud.Minimum, nud.Value); + } } } \ No newline at end of file