diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs
index 81a21955c4..281e9f6b61 100644
--- a/src/Avalonia.Base/Data/Core/BindingExpression.cs
+++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs
@@ -205,7 +205,9 @@ internal class BindingExpression : UntypedBindingExpressionBase, IDescription, I
var error = dataValidationError is not null ?
new BindingError(dataValidationError, BindingErrorType.DataValidationError) :
null;
- ConvertAndPublishValue(value, error);
+
+ var forceUpdate = _mode == BindingMode.OneWay;
+ ConvertAndPublishValue(value, error, forceUpdate);
}
}
else if (_mode == BindingMode.OneWayToSource && nodeIndex == _nodes.Count - 2 && value is not null)
@@ -402,7 +404,7 @@ internal class BindingExpression : UntypedBindingExpressionBase, IDescription, I
error);
}
- private void ConvertAndPublishValue(object? value, BindingError? error)
+ private void ConvertAndPublishValue(object? value, BindingError? error, bool forceUpdate = false)
{
var isTargetNullValue = false;
@@ -450,7 +452,7 @@ internal class BindingExpression : UntypedBindingExpressionBase, IDescription, I
value = ConvertFallback(FallbackValue, nameof(FallbackValue));
// Publish the value.
- PublishValue(value, error);
+ PublishValue(value, error, forceUpdate);
}
private void WriteTargetValueToSource()
diff --git a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
index e45c88c457..874c3c2e13 100644
--- a/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
+++ b/src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs
@@ -406,7 +406,8 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase,
///
/// The new value, or .
/// The new binding or data validation error.
- private protected void PublishValue(object? value, BindingError? error = null)
+ /// If true, forces the value to be published even if it hasn't changed.
+ private protected void PublishValue(object? value, BindingError? error = null, bool forceUpdate = false)
{
Debug.Assert(value is not BindingNotification);
Debug.Assert(value != BindingOperations.DoNothing);
@@ -424,7 +425,7 @@ public abstract class UntypedBindingExpressionBase : BindingExpressionBase,
value = null;
}
- var hasValueChanged = value != UnchangedValue && !TypeUtilities.IdentityEquals(value, GetValue(), TargetType);
+ var hasValueChanged = forceUpdate || (value != UnchangedValue && !TypeUtilities.IdentityEquals(value, GetValue(), TargetType));
var hasErrorChanged = error is not null || _error is not null;
if (hasValueChanged)
diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs
index cf80b86386..c933eb5316 100644
--- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs
+++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.Mode.cs
@@ -1,3 +1,5 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
using Avalonia.Data;
using Xunit;
@@ -201,4 +203,23 @@ public partial class BindingExpressionTests
Assert.Equal("foo", data.StringValue);
}
+
+ [Fact]
+ public void OneWay_Binding_Updates_Target_When_Changes_And_Source_Raises_PropertyChanged()
+ {
+ var data = new ViewModel { StringValue = "foo" };
+ var target = CreateTarget(
+ x => x.StringValue,
+ dataContext: data,
+ mode: BindingMode.OneWay);
+
+ Assert.Equal("foo", target.String);
+
+ target.SetCurrentValue(TargetClass.StringProperty, "bar");
+ Assert.Equal("bar", target.String);
+
+ data.RaisePropertyChanged(nameof(data.StringValue));
+
+ Assert.Equal("foo", target.String);
+ }
}