From c62e1dffffeea03a3a36296069d66e38c6a203ae Mon Sep 17 00:00:00 2001 From: Evan <109839359+Evan260@users.noreply.github.com> Date: Tue, 3 Mar 2026 09:47:38 -0600 Subject: [PATCH] Fix TextBox validation error persisting when reverting to same valid value (#20780) * Fix TextBox validation error persisting when reverting to same valid value When a TwoWay binding with validation had an error and the user set the target back to the same value that was last successfully written to the source, WriteTargetValueToSource() would bail out early because the target value matched LeafNode.Value. This prevented WriteValueToSource() from being called, so the validation error was never cleared. Add an ErrorType check to the condition in WriteTargetValueToSource() so that the write-through still happens when there is a pending validation error, matching the pattern already used in WriteValueToSource() at line 310. Fixes #20534 https://claude.ai/code/session_01LTinLuE1bsNKaLPuDSNuuw * Add test for validation error clearing when reverting to same valid value Reproduces the scenario from #20534 where a TwoWay binding with a setter that throws on invalid values fails to clear the validation error when the target is set back to the same value that was last successfully written to the source. https://claude.ai/code/session_01LTinLuE1bsNKaLPuDSNuuw * Remove redundant checks in WriteTargetValueToSource() --- .../Data/Core/BindingExpression.cs | 7 ++-- .../BindingExpressionTests.DataValidation.cs | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index 00a90f87fe..81a21955c4 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -460,12 +460,9 @@ internal class BindingExpression : UntypedBindingExpressionBase, IDescription, I StopDelayTimer(); if (TryGetTarget(out var target) && - TargetProperty is not null && - target.GetValue(TargetProperty) is var value && - LeafNode is { } leafNode && - !TypeUtilities.IdentityEquals(value, leafNode.Value, TargetType)) + TargetProperty is not null) { - WriteValueToSource(value); + WriteValueToSource(target.GetValue(TargetProperty)); } } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs index 04aa7b8de2..1575bf6ad3 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.DataValidation.cs @@ -133,6 +133,42 @@ public partial class BindingExpressionTests GC.KeepAlive(data); } + [Fact] + public void Setter_Exception_Error_Is_Cleared_When_Reverting_To_Same_Valid_Value() + { + // Issue #20534: When a setter throws on an invalid value and the user + // reverts to the same valid value that was last successfully set, the + // validation error should be cleared. + var data = new ExceptionViewModel { MustBePositive = 5 }; + + var target = CreateTargetWithSource( + data, + o => o.MustBePositive, + enableDataValidation: true, + mode: BindingMode.TwoWay); + + // Step 1: Set a valid value. + target.Int = 10; + Assert.Equal(10, data.MustBePositive); + AssertNoError(target, TargetClass.IntProperty); + + // Step 2: Set an invalid value — setter throws, error appears. + target.Int = -5; + Assert.Equal(10, data.MustBePositive); + AssertBindingError( + target, + TargetClass.IntProperty, + new ArgumentOutOfRangeException("value"), + BindingErrorType.DataValidationError); + + // Step 3: Revert to the same valid value (10). The error must clear. + target.Int = 10; + Assert.Equal(10, data.MustBePositive); + AssertNoError(target, TargetClass.IntProperty); + + GC.KeepAlive(data); + } + [Fact] public void Indei_Validation_Does_Not_Subscribe_When_DataValidation_Not_Enabled() {