From a3dea23560c04c1d603f91af1d6fe65e0e949254 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 23 Jun 2018 02:28:12 +0200 Subject: [PATCH] Use LightweightObservableBase for BindingExpression. --- .../Data/Core/BindingExpression.cs | 43 ++++++++++++++++--- .../Data/Core/BindingExpressionTests.cs | 15 +++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs index 4b41d1568c..911c621fb7 100644 --- a/src/Avalonia.Base/Data/Core/BindingExpression.cs +++ b/src/Avalonia.Base/Data/Core/BindingExpression.cs @@ -7,6 +7,7 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using Avalonia.Data.Converters; using Avalonia.Logging; +using Avalonia.Reactive; using Avalonia.Utilities; namespace Avalonia.Data.Core @@ -15,13 +16,14 @@ namespace Avalonia.Data.Core /// Binds to an expression on an object using a type value converter to convert the values /// that are send and received. /// - public class BindingExpression : ISubject, IDescription + public class BindingExpression : LightweightObservableBase, ISubject, IDescription { private readonly ExpressionObserver _inner; private readonly Type _targetType; private readonly object _fallbackValue; private readonly BindingPriority _priority; - private readonly Subject _errors = new Subject(); + InnerListener _innerListener; + WeakReference _value; /// /// Initializes a new instance of the class. @@ -139,7 +141,7 @@ namespace Avalonia.Data.Core "IValueConverter should not return non-errored BindingNotification."); } - _errors.OnNext(notification); + PublishNext(notification); if (_fallbackValue != AvaloniaProperty.UnsetValue) { @@ -170,12 +172,18 @@ namespace Avalonia.Data.Core } } - /// - public IDisposable Subscribe(IObserver observer) + protected override void Initialize() => _innerListener = new InnerListener(this); + protected override void Deinitialize() => _innerListener.Dispose(); + + protected override void Subscribed(IObserver observer, bool first) { - return _inner.Select(ConvertValue).Merge(_errors).Subscribe(observer); + if (!first && _value != null && _value.TryGetTarget(out var val) == true) + { + observer.OnNext(val); + } } + /// private object ConvertValue(object value) { var notification = value as BindingNotification; @@ -301,5 +309,28 @@ namespace Avalonia.Data.Core return a; } + + public class InnerListener : IObserver, IDisposable + { + private readonly BindingExpression _owner; + private readonly IDisposable _dispose; + + public InnerListener(BindingExpression owner) + { + _owner = owner; + _dispose = owner._inner.Subscribe(this); + } + + public void Dispose() => _dispose.Dispose(); + public void OnCompleted() => _owner.PublishCompleted(); + public void OnError(Exception error) => _owner.PublishError(error); + + public void OnNext(object value) + { + var converted = _owner.ConvertValue(value); + _owner._value = new WeakReference(converted); + _owner.PublishNext(converted); + } + } } } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs index 6b71d28e22..81acd6a087 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs @@ -337,6 +337,21 @@ namespace Avalonia.Base.UnitTests.Data.Core GC.KeepAlive(data); } + [Fact] + public void Second_Subscription_Should_Fire_Immediately() + { + var data = new Class1 { StringValue = "foo" }; + var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(string)); + object result = null; + + target.Subscribe(); + target.Subscribe(x => result = x); + + Assert.Equal("foo", result); + + GC.KeepAlive(data); + } + private class Class1 : NotifyingBase { private string _stringValue;