From f6fe68eddbf9558e7135a7c886b0994f1ac3711a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 6 Feb 2024 21:57:29 +0100 Subject: [PATCH] Fix OneWayToSource bindings with read-only properties. (#14513) * Added more OneWayToSource tests. One of whom is failing. * Don't public value for OneWayToSource bindings. Looks to have been a brainfart. This allows `OneWayToSource` bindings to read-only properties. --- .../Data/Core/BindingExpression.cs | 3 - .../Data/Core/BindingExpressionTests.Mode.cs | 67 +++++++++++++++++++ .../Data/Core/BindingExpressionTests.cs | 14 ++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index 20cca96b30..3371e510e3 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -375,9 +375,6 @@ internal partial class BindingExpression : UntypedBindingExpressionBase, IDescri TryGetTarget(out var target) && TargetProperty is not null) { - if (_mode is BindingMode.OneWayToSource) - PublishValue(target.GetValue(TargetProperty)); - var trigger = UpdateSourceTrigger; if (trigger is UpdateSourceTrigger.PropertyChanged) diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs index 3cbd5eae1a..f78630bd07 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs @@ -1,5 +1,6 @@ using Avalonia.Data; using Xunit; +using Xunit.Sdk; #nullable enable @@ -67,4 +68,70 @@ public partial class BindingExpressionTests data2.DoubleValue = 0.2; Assert.Equal(0.5, target.Double); } + + [Fact] + public void OneWayToSource_Binding_Updates_Source_When_Target_Changes() + { + var data = new ViewModel(); + var target = CreateTarget( + x => x.StringValue, + dataContext: data, + mode: BindingMode.OneWayToSource); + + Assert.Null(data.StringValue); + + target.String = "foo"; + + Assert.Equal("foo", data.StringValue); + } + + [Fact] + public void OneWayToSource_Binding_Does_Not_Update_Target_When_Source_Changes() + { + var data = new ViewModel(); + var target = CreateTarget( + x => x.StringValue, + dataContext: data, + mode: BindingMode.OneWayToSource); + + target.String = "foo"; + Assert.Equal("foo", data.StringValue); + + data.StringValue = "bar"; + Assert.Equal("foo", target.String); + } + + [Fact] + public void OneWayToSource_Binding_Updates_Source_When_DataContext_Changes() + { + var data1 = new ViewModel(); + var data2 = new ViewModel(); + var target = CreateTarget( + x => x.StringValue, + dataContext: data1, + mode: BindingMode.OneWayToSource); + + target.String = "foo"; + Assert.Equal("foo", data1.StringValue); + + target.DataContext = data2; + Assert.Equal("foo", data2.StringValue); + } + + [Fact] + public void Can_Bind_Readonly_Property_OneWayToSource() + { + var data = new ViewModel(); + var target = CreateTarget( + x => x.StringValue, + dataContext: data, + mode: BindingMode.OneWayToSource, + targetProperty: TargetClass.ReadOnlyStringProperty); + + Assert.Equal("readonly", data.StringValue); + + target.SetReadOnlyString("foo"); + + Assert.Equal("foo", data.StringValue); + } } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs index 6a83f34132..759337b430 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs @@ -343,6 +343,12 @@ public abstract partial class BindingExpressionTests AvaloniaProperty.Register("Object"); public static readonly StyledProperty StringProperty = AvaloniaProperty.Register("String"); + public static readonly DirectProperty ReadOnlyStringProperty = + AvaloniaProperty.RegisterDirect( + nameof(ReadOnlyString), + o => o.ReadOnlyString); + + private string? _readOnlyString = "readonly"; static TargetClass() { @@ -379,10 +385,18 @@ public abstract partial class BindingExpressionTests set => SetValue(StringProperty, value); } + public string? ReadOnlyString + { + get => _readOnlyString; + private set => SetAndRaise(ReadOnlyStringProperty, ref _readOnlyString, value); + } + public Dictionary BindingNotifications { get; } = new(); public override string ToString() => nameof(TargetClass); + public void SetReadOnlyString(string? value) => ReadOnlyString = value; + protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error) { base.UpdateDataValidation(property, state, error);