From b6c131477e47a6ef9b3af27a0ea68ce303ae8f2d Mon Sep 17 00:00:00 2001 From: Kevin Bost Date: Thu, 4 Jun 2026 23:46:28 -0700 Subject: [PATCH] ``` fix: Prevent NumericUpDown from resetting text input during typing ``` ``` Introduces a flag to temporarily disable the text box's value update while the user is actively typing. This prevents the control from prematurely overwriting partial user input, which previously made it difficult to enter values when a minimum or maximum was set. Resolves #4061 ``` --- src/MaterialDesignThemes.Wpf/UpDownBase.cs | 11 +++++++-- .../WPF/UpDownControls/NumericUpDownTests.cs | 24 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/MaterialDesignThemes.Wpf/UpDownBase.cs b/src/MaterialDesignThemes.Wpf/UpDownBase.cs index 0617eb7824..f16e6afc35 100644 --- a/src/MaterialDesignThemes.Wpf/UpDownBase.cs +++ b/src/MaterialDesignThemes.Wpf/UpDownBase.cs @@ -47,6 +47,8 @@ private static bool TryParse(string text, IFormatProvider? formatProvider, [NotN private static int Compare(T value1, T value2) => _arithmetic.Compare(value1, value2); #endif + private bool _isTextChanging; + #region DependencyProperties #region DependencyProperty : MinimumProperty @@ -116,7 +118,7 @@ private static void OnNumericValueChanged(DependencyObject d, DependencyProperty }; upDownBase.RaiseEvent(args); - if (upDownBase._textBoxField is { } textBox) + if (!upDownBase._isTextChanging && upDownBase._textBoxField is { } textBox) { textBox.Text = Convert.ToString(e.NewValue, CultureInfo.CurrentCulture); } @@ -196,10 +198,13 @@ public event RoutedPropertyChangedEventHandler ValueChanged public override void OnApplyTemplate() { if (_increaseButton != null) + { _increaseButton.Click -= IncreaseButtonOnClick; - + } if (_decreaseButton != null) + { _decreaseButton.Click -= DecreaseButtonOnClick; + } if (_textBoxField != null) { _textBoxField.TextChanged -= OnTextBoxTextChanged; @@ -243,7 +248,9 @@ private void OnTextBoxTextChanged(object sender, EventArgs e) { if (TryParse(textBoxField.Text, CultureInfo.CurrentCulture, out T? value)) { + _isTextChanging = true; SetCurrentValue(ValueProperty, ClampValue(value)); + _isTextChanging = false; } } } diff --git a/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/NumericUpDownTests.cs b/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/NumericUpDownTests.cs index c0f2d8f317..21a5ef4553 100644 --- a/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/NumericUpDownTests.cs +++ b/tests/MaterialDesignThemes.UITests/WPF/UpDownControls/NumericUpDownTests.cs @@ -213,6 +213,30 @@ public async Task NumericUpDown_WhenBindingUpdateTriggerIsPropertyChanged_ItUpda await Assert.That(tag?.ToString()).IsEqualTo("4"); } + [Test] + [Description("Issue 4061")] + public async Task NumericUpDown_WhenMinimumIsPositiveNumber_ItAllowsTextToBeChangedWithoutClamping() + { + //Arrange + //NB: Value should be something that is not equal to the minimum + var numericUpDown = await LoadXaml(""" + + """); + + var textBox = await numericUpDown.GetElement("PART_TextBox"); + + //Act + await textBox.MoveKeyboardFocus(); + await textBox.SendKeyboardInput($"{ModifierKeys.Control}{Key.A}{ModifierKeys.None}8"); + object? firstValue = await numericUpDown.GetTag(); + await textBox.SendKeyboardInput($"7"); + object? secondValue = await numericUpDown.GetTag(); + + //Assert + await Assert.That(firstValue?.ToString()).IsEqualTo("60"); + await Assert.That(secondValue?.ToString()).IsEqualTo("87"); + } + [Test] [Description("Issue 3827")] public async Task NumericUpDown_WhenBindingUpdateTriggerIsLostFocus_ItDoesNotUpdateUntilItLoosesFocus()