Skip to content

Commit 0d83bc1

Browse files
committed
Fixes NumericUpDown text update issues
Fixes an issue where the NumericUpDown control was not properly updating the text in the TextBox when focus was lost after invalid input. Adds a LostFocus event handler to update the textbox value. Adds UI tests to verify that values are updated as expected.
1 parent 52af8cc commit 0d83bc1

File tree

3 files changed

+93
-9
lines changed

3 files changed

+93
-9
lines changed

src/MaterialDesignThemes.Wpf/UpDownBase.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,10 @@ public override void OnApplyTemplate()
201201
if (_decreaseButton != null)
202202
_decreaseButton.Click -= DecreaseButtonOnClick;
203203
if (_textBoxField != null)
204+
{
204205
_textBoxField.TextChanged -= OnTextBoxTextChanged;
206+
_textBoxField.LostFocus -= OnTextBoxLostFocus;
207+
}
205208

206209
base.OnApplyTemplate();
207210

@@ -220,11 +223,20 @@ public override void OnApplyTemplate()
220223
if (_textBoxField != null)
221224
{
222225
_textBoxField.TextChanged += OnTextBoxTextChanged;
226+
_textBoxField.LostFocus += OnTextBoxLostFocus;
223227
_textBoxField.Text = Value?.ToString();
224228
}
225229

226230
}
227231

232+
private void OnTextBoxLostFocus(object sender, EventArgs e)
233+
{
234+
if (_textBoxField is { } textBoxField)
235+
{
236+
textBoxField.Text = Value?.ToString();
237+
}
238+
}
239+
228240
private void OnTextBoxTextChanged(object sender, EventArgs e)
229241
{
230242
if (_textBoxField is { } textBoxField)
@@ -233,8 +245,6 @@ private void OnTextBoxTextChanged(object sender, EventArgs e)
233245
{
234246
SetCurrentValue(ValueProperty, ClampValue(value));
235247
}
236-
//NB: Because setting ValueProperty will coerce the value, we re-assign back to the textbox here.
237-
textBoxField.Text = Value?.ToString();
238248
}
239249
}
240250

tests/MaterialDesignThemes.UITests/WPF/UpDownControls/DecimalUpDownTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ public async Task IncreaseButtonClickWhenTextIsAboveMaximum_DoesNotIncreaseValue
187187

188188
[Theory]
189189
[Description("Issue 3781")]
190-
[InlineData("30")]
191-
[InlineData("abc")]
192-
[InlineData("2a")]
193-
public async Task LostFocusWhenTextIsInvalid_RevertsToOriginalValue(string inputText)
190+
[InlineData("30", 2.5)]
191+
[InlineData("abc", 2.5)]
192+
[InlineData("2a", 2)]
193+
public async Task LostFocusWhenTextIsInvalid_RevertsToOriginalValue(string inputText, decimal expectedValue)
194194
{
195195
await using var recorder = new TestRecorder(App);
196196

@@ -209,8 +209,8 @@ public async Task LostFocusWhenTextIsInvalid_RevertsToOriginalValue(string input
209209
await textBox.SendKeyboardInput($"{ModifierKeys.Control}{Key.A}{ModifierKeys.None}{inputText}");
210210
await button.MoveKeyboardFocus();
211211

212-
Assert.Equal("2.5", await textBox.GetText());
213-
Assert.Equal(2.5m, await decimalUpDown.GetValue());
212+
Assert.Equal(expectedValue.ToString(), await textBox.GetText());
213+
Assert.Equal(expectedValue, await decimalUpDown.GetValue());
214214

215215
recorder.Success();
216216
}

tests/MaterialDesignThemes.UITests/WPF/UpDownControls/NumericUpDownTests.cs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
using System.ComponentModel;
1+
using System;
2+
using System.ComponentModel;
3+
using System.Windows.Controls;
4+
using System.Windows.Data;
5+
using Google.Protobuf.WellKnownTypes;
26
using MaterialDesignThemes.UITests.Samples.UpDownControls;
37

48
namespace MaterialDesignThemes.UITests.WPF.UpDownControls;
@@ -215,4 +219,74 @@ public async Task NumericUpDown_WhenValueEqualsMinimum_DisableButtons(int value,
215219

216220
recorder.Success();
217221
}
222+
223+
[Fact]
224+
[Description("Issue 3827")]
225+
public async Task NumericUpDown_WhenBindingUpdateTriggerIsPropertyChanged_ItUpdatesBeforeLoosingFocus()
226+
{
227+
await using var recorder = new TestRecorder(App);
228+
//Arrange
229+
var numericUpDown = await LoadXaml<NumericUpDown>("""
230+
<materialDesign:NumericUpDown Tag="{Binding Value, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}" Maximum="10" Minimum="1" />
231+
""");
232+
233+
var textBox = await numericUpDown.GetElement<TextBox>("PART_TextBox");
234+
//Act
235+
await textBox.MoveKeyboardFocus();
236+
await textBox.SendKeyboardInput($"{ModifierKeys.Control}{Key.A}{ModifierKeys.None}4");
237+
238+
//Act
239+
object? tag = await numericUpDown.GetTag();
240+
241+
//Assert
242+
Assert.Equal("4", tag?.ToString());
243+
244+
recorder.Success();
245+
}
246+
247+
[Fact]
248+
[Description("Issue 3827")]
249+
public async Task NumericUpDown_WhenBindingUpdateTriggerIsLostFocus_ItDoesNotUpdateUntilItLoosesFocus()
250+
{
251+
await using var recorder = new TestRecorder(App);
252+
//Arrange
253+
var userControl = await LoadUserControl<BoundNumericUpDown>();
254+
var numericUpDown = await userControl.GetElement<NumericUpDown>();
255+
var buttonToFocus = await userControl.GetElement<Button>("btnToFocus");
256+
await numericUpDown.SetValue(2);
257+
258+
static void SetBindingToLostFocus(NumericUpDown numericUpDown)
259+
{
260+
var binding = new Binding(nameof(NumericUpDown.Value))
261+
{
262+
Path = new(nameof(BoundNumericUpDownViewModel.Value)),
263+
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
264+
};
265+
BindingOperations.SetBinding(numericUpDown, NumericUpDown.ValueProperty, binding);
266+
}
267+
await numericUpDown.RemoteExecute(SetBindingToLostFocus);
268+
269+
var textBox = await numericUpDown.GetElement<TextBox>("PART_TextBox");
270+
271+
static int GetViewModelValue(NumericUpDown numericUpDown)
272+
{
273+
return ((BoundNumericUpDownViewModel)numericUpDown.DataContext).Value;
274+
}
275+
276+
//Act
277+
await textBox.MoveKeyboardFocus();
278+
await textBox.SendKeyboardInput($"{ModifierKeys.Control}{Key.A}{ModifierKeys.None}4");
279+
280+
//Act
281+
int valueBeforeLostFocus = await numericUpDown.RemoteExecute(GetViewModelValue);
282+
await textBox.SendKeyboardInput($"{Key.Tab}");
283+
int valueAfterLostFocus = await numericUpDown.RemoteExecute(GetViewModelValue);
284+
285+
286+
//Assert
287+
Assert.Equal("2", valueBeforeLostFocus.ToString());
288+
Assert.Equal("4", valueAfterLostFocus.ToString());
289+
290+
recorder.Success();
291+
}
218292
}

0 commit comments

Comments
 (0)