Skip to content

UpDownBase: Fix event subscription mismatch #3844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/MaterialDesignThemes.Wpf/UpDownBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ public override void OnApplyTemplate()
if (_decreaseButton != null)
_decreaseButton.Click -= DecreaseButtonOnClick;
if (_textBoxField != null)
_textBoxField.TextChanged -= OnTextBoxFocusLost;
{
_textBoxField.TextChanged -= OnTextBoxTextChanged;
_textBoxField.LostFocus -= OnTextBoxLostFocus;
}

base.OnApplyTemplate();

Expand All @@ -219,22 +222,29 @@ public override void OnApplyTemplate()

if (_textBoxField != null)
{
_textBoxField.LostFocus += OnTextBoxFocusLost;
_textBoxField.TextChanged += OnTextBoxTextChanged;
_textBoxField.LostFocus += OnTextBoxLostFocus;
_textBoxField.Text = Value?.ToString();
}

}

private void OnTextBoxFocusLost(object sender, EventArgs e)
private void OnTextBoxLostFocus(object sender, EventArgs e)
{
if (_textBoxField is { } textBoxField)
{
textBoxField.Text = Value?.ToString();
}
}

private void OnTextBoxTextChanged(object sender, EventArgs e)
{
if (_textBoxField is { } textBoxField)
{
if (TryParse(textBoxField.Text, CultureInfo.CurrentUICulture, out T? value))
{
SetCurrentValue(ValueProperty, ClampValue(value));
}
//NB: Because setting ValueProperty will coerce the value, we re-assign back to the textbox here.
textBoxField.Text = Value?.ToString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ public async Task IncreaseButtonClickWhenTextIsAboveMaximum_DoesNotIncreaseValue

[Theory]
[Description("Issue 3781")]
[InlineData("30")]
[InlineData("abc")]
[InlineData("2a")]
public async Task LostFocusWhenTextIsInvalid_RevertsToOriginalValue(string inputText)
[InlineData("30", 2.5)]
[InlineData("abc", 2.5)]
[InlineData("2a", 2)]
public async Task LostFocusWhenTextIsInvalid_RevertsToOriginalValue(string inputText, decimal expectedValue)
{
await using var recorder = new TestRecorder(App);

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

Assert.Equal("2.5", await textBox.GetText());
Assert.Equal(2.5m, await decimalUpDown.GetValue());
Assert.Equal(expectedValue.ToString(), await textBox.GetText());
Assert.Equal(expectedValue, await decimalUpDown.GetValue());

recorder.Success();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Data;
using Google.Protobuf.WellKnownTypes;
using MaterialDesignThemes.UITests.Samples.UpDownControls;

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

recorder.Success();
}

[Fact]
[Description("Issue 3827")]
public async Task NumericUpDown_WhenBindingUpdateTriggerIsPropertyChanged_ItUpdatesBeforeLoosingFocus()
{
await using var recorder = new TestRecorder(App);
//Arrange
var numericUpDown = await LoadXaml<NumericUpDown>("""
<materialDesign:NumericUpDown Tag="{Binding Value, RelativeSource={RelativeSource Self}, UpdateSourceTrigger=PropertyChanged}" Maximum="10" Minimum="1" />
""");

var textBox = await numericUpDown.GetElement<TextBox>("PART_TextBox");
//Act
await textBox.MoveKeyboardFocus();
await textBox.SendKeyboardInput($"{ModifierKeys.Control}{Key.A}{ModifierKeys.None}4");

//Act
object? tag = await numericUpDown.GetTag();

//Assert
Assert.Equal("4", tag?.ToString());

recorder.Success();
}

[Fact]
[Description("Issue 3827")]
public async Task NumericUpDown_WhenBindingUpdateTriggerIsLostFocus_ItDoesNotUpdateUntilItLoosesFocus()
{
await using var recorder = new TestRecorder(App);
//Arrange
var userControl = await LoadUserControl<BoundNumericUpDown>();
var numericUpDown = await userControl.GetElement<NumericUpDown>();
var buttonToFocus = await userControl.GetElement<Button>("btnToFocus");
await numericUpDown.SetValue(2);

static void SetBindingToLostFocus(NumericUpDown numericUpDown)
{
var binding = new Binding(nameof(NumericUpDown.Value))
{
Path = new(nameof(BoundNumericUpDownViewModel.Value)),
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
};
BindingOperations.SetBinding(numericUpDown, NumericUpDown.ValueProperty, binding);
}
await numericUpDown.RemoteExecute(SetBindingToLostFocus);

var textBox = await numericUpDown.GetElement<TextBox>("PART_TextBox");

static int GetViewModelValue(NumericUpDown numericUpDown)
{
return ((BoundNumericUpDownViewModel)numericUpDown.DataContext).Value;
}

//Act
await textBox.MoveKeyboardFocus();
await textBox.SendKeyboardInput($"{ModifierKeys.Control}{Key.A}{ModifierKeys.None}4");

//Act
int valueBeforeLostFocus = await numericUpDown.RemoteExecute(GetViewModelValue);
await textBox.SendKeyboardInput($"{Key.Tab}");
int valueAfterLostFocus = await numericUpDown.RemoteExecute(GetViewModelValue);


//Assert
Assert.Equal("2", valueBeforeLostFocus.ToString());
Assert.Equal("4", valueAfterLostFocus.ToString());

recorder.Success();
}
}
Loading