From 2ba45511d04d9de8fad15a0f3889c73fa8c10dbd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Jun 2024 11:31:55 +0200 Subject: [PATCH] Make bindings react to PropertyChanged even if property hasn't changed (#16150) * Added failing test for #16137. * Raise node change even if value hasn't changed. Fixes #16137. --- .../Core/ExpressionNodes/ExpressionNode.cs | 20 ++----------------- .../Core/BindingExpressionTests.Property.cs | 20 +++++++++++++++++++ .../Data/Core/BindingExpressionTests.cs | 10 ++++++++-- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs index ee52308756..7a8ab69e6b 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs @@ -211,24 +211,8 @@ internal abstract class ExpressionNode protected void SetValue(object? value, Exception? dataValidationError = null) { Debug.Assert(value is not BindingNotification); - - if (Owner is null) - return; - - // We raise a change notification if: - // - // - This is the initial value (_value is null) - // - There is a data validation error - // - There is no data validation error, but the owner has one - // - The new value is different to the old value - if (_value is null || - dataValidationError is not null || - (dataValidationError is null && Owner.ErrorType == BindingErrorType.DataValidationError) || - !Equals(value, _value)) - { - _value = value; - Owner.OnNodeValueChanged(Index, value, dataValidationError); - } + _value = value; + Owner?.OnNodeValueChanged(Index, value, dataValidationError); } /// diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Property.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Property.cs index 225b2be450..521555488b 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Property.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Property.cs @@ -226,6 +226,26 @@ namespace Avalonia.Base.UnitTests.Data.Core GC.KeepAlive(data); } + [Fact] + public void Converter_Should_Be_Called_On_PropertyChanged_Even_If_Property_Not_Changed() + { + // Issue #16137 + var data = new ViewModel(); + var converter = new PrefixConverter("foo"); + var target = CreateTargetWithSource( + data, + o => o.IntValue, + converter: converter, + targetProperty: TargetClass.StringProperty); + + Assert.Equal("foo0", target.String); + + converter.Prefix = "bar"; + data.RaisePropertyChanged(nameof(data.IntValue)); + + Assert.Equal("bar0", target.String); + } + private class DerivedViewModel : ViewModel { } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs index 805655d6ab..a838029175 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs @@ -417,14 +417,20 @@ public abstract partial class BindingExpressionTests protected class PrefixConverter : IValueConverter { + public PrefixConverter(string? prefix = null) => Prefix = prefix; + + public string? Prefix { get; set; } + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (targetType != typeof(string)) return value; var result = value?.ToString() ?? string.Empty; - if (parameter is not null) - result = parameter.ToString() + result; + var prefix = parameter?.ToString() ?? Prefix; + + if (prefix is not null) + result = prefix + result; return result; }