Browse Source

Use LightweightObservableBase for BindingExpression.

pull/1690/head
Steven Kirk 8 years ago
parent
commit
a3dea23560
  1. 43
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  2. 15
      tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs

43
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.
/// </summary>
public class BindingExpression : ISubject<object>, IDescription
public class BindingExpression : LightweightObservableBase<object>, ISubject<object>, IDescription
{
private readonly ExpressionObserver _inner;
private readonly Type _targetType;
private readonly object _fallbackValue;
private readonly BindingPriority _priority;
private readonly Subject<object> _errors = new Subject<object>();
InnerListener _innerListener;
WeakReference<object> _value;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> 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
}
}
/// <inheritdoc/>
public IDisposable Subscribe(IObserver<object> observer)
protected override void Initialize() => _innerListener = new InnerListener(this);
protected override void Deinitialize() => _innerListener.Dispose();
protected override void Subscribed(IObserver<object> observer, bool first)
{
return _inner.Select(ConvertValue).Merge(_errors).Subscribe(observer);
if (!first && _value != null && _value.TryGetTarget(out var val) == true)
{
observer.OnNext(val);
}
}
/// <inheritdoc/>
private object ConvertValue(object value)
{
var notification = value as BindingNotification;
@ -301,5 +309,28 @@ namespace Avalonia.Data.Core
return a;
}
public class InnerListener : IObserver<object>, 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<object>(converted);
_owner.PublishNext(converted);
}
}
}
}

15
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;

Loading…
Cancel
Save