|
|
@@ -2,22 +2,18 @@
|
|
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|
|
|
|
|
using System;
|
|
|
-using System.Reactive.Disposables;
|
|
|
-using System.Reactive.Linq;
|
|
|
-using System.Reactive.Subjects;
|
|
|
-using Avalonia.Data;
|
|
|
|
|
|
namespace Avalonia.Data.Core
|
|
|
{
|
|
|
- internal abstract class ExpressionNode : ISubject<object>
|
|
|
+ internal abstract class ExpressionNode
|
|
|
{
|
|
|
private static readonly object CacheInvalid = new object();
|
|
|
protected static readonly WeakReference UnsetReference =
|
|
|
new WeakReference(AvaloniaProperty.UnsetValue);
|
|
|
|
|
|
private WeakReference _target = UnsetReference;
|
|
|
- private IDisposable _valueSubscription;
|
|
|
- private IObserver<object> _observer;
|
|
|
+ private Action<object> _subscriber;
|
|
|
+ private bool _listening;
|
|
|
|
|
|
protected WeakReference LastValue { get; private set; }
|
|
|
|
|
|
@@ -33,92 +29,66 @@ namespace Avalonia.Data.Core
|
|
|
|
|
|
var oldTarget = _target?.Target;
|
|
|
var newTarget = value.Target;
|
|
|
- var running = _valueSubscription != null;
|
|
|
|
|
|
if (!ReferenceEquals(oldTarget, newTarget))
|
|
|
{
|
|
|
- _valueSubscription?.Dispose();
|
|
|
- _valueSubscription = null;
|
|
|
+ if (_listening)
|
|
|
+ {
|
|
|
+ StopListening();
|
|
|
+ }
|
|
|
+
|
|
|
_target = value;
|
|
|
|
|
|
- if (running)
|
|
|
+ if (_subscriber != null)
|
|
|
{
|
|
|
- _valueSubscription = StartListening();
|
|
|
+ StartListening();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public IDisposable Subscribe(IObserver<object> observer)
|
|
|
+ public void Subscribe(Action<object> subscriber)
|
|
|
{
|
|
|
- if (_observer != null)
|
|
|
+ if (_subscriber != null)
|
|
|
{
|
|
|
throw new AvaloniaInternalException("ExpressionNode can only be subscribed once.");
|
|
|
}
|
|
|
|
|
|
- _observer = observer;
|
|
|
- var nextSubscription = Next?.Subscribe(this);
|
|
|
- _valueSubscription = StartListening();
|
|
|
-
|
|
|
- return Disposable.Create(() =>
|
|
|
- {
|
|
|
- _valueSubscription?.Dispose();
|
|
|
- _valueSubscription = null;
|
|
|
- LastValue = null;
|
|
|
- nextSubscription?.Dispose();
|
|
|
- _observer = null;
|
|
|
- });
|
|
|
+ _subscriber = subscriber;
|
|
|
+ Next?.Subscribe(NextValueChanged);
|
|
|
+ StartListening();
|
|
|
}
|
|
|
|
|
|
- void IObserver<object>.OnCompleted()
|
|
|
+ public void Unsubscribe()
|
|
|
{
|
|
|
- throw new AvaloniaInternalException("ExpressionNode.OnCompleted should not be called.");
|
|
|
- }
|
|
|
+ Next?.Unsubscribe();
|
|
|
|
|
|
- void IObserver<object>.OnError(Exception error)
|
|
|
- {
|
|
|
- throw new AvaloniaInternalException("ExpressionNode.OnError should not be called.");
|
|
|
+ if (_listening)
|
|
|
+ {
|
|
|
+ StopListening();
|
|
|
+ }
|
|
|
+
|
|
|
+ LastValue = null;
|
|
|
+ _subscriber = null;
|
|
|
}
|
|
|
|
|
|
- void IObserver<object>.OnNext(object value)
|
|
|
+ protected virtual void StartListeningCore(WeakReference reference)
|
|
|
{
|
|
|
- NextValueChanged(value);
|
|
|
+ ValueChanged(reference.Target);
|
|
|
}
|
|
|
|
|
|
- protected virtual IObservable<object> StartListeningCore(WeakReference reference)
|
|
|
+ protected virtual void StopListeningCore()
|
|
|
{
|
|
|
- return Observable.Return(reference.Target);
|
|
|
}
|
|
|
|
|
|
protected virtual void NextValueChanged(object value)
|
|
|
{
|
|
|
var bindingBroken = BindingNotification.ExtractError(value) as MarkupBindingChainException;
|
|
|
bindingBroken?.AddNode(Description);
|
|
|
- _observer.OnNext(value);
|
|
|
- }
|
|
|
-
|
|
|
- private IDisposable StartListening()
|
|
|
- {
|
|
|
- var target = _target.Target;
|
|
|
- IObservable<object> source;
|
|
|
-
|
|
|
- if (target == null)
|
|
|
- {
|
|
|
- source = Observable.Return(TargetNullNotification());
|
|
|
- }
|
|
|
- else if (target == AvaloniaProperty.UnsetValue)
|
|
|
- {
|
|
|
- source = Observable.Empty<object>();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- source = StartListeningCore(_target);
|
|
|
- }
|
|
|
-
|
|
|
- return source.Subscribe(ValueChanged);
|
|
|
+ _subscriber(value);
|
|
|
}
|
|
|
|
|
|
- private void ValueChanged(object value)
|
|
|
+ protected void ValueChanged(object value)
|
|
|
{
|
|
|
var notification = value as BindingNotification;
|
|
|
|
|
|
@@ -131,24 +101,50 @@ namespace Avalonia.Data.Core
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- _observer.OnNext(value);
|
|
|
+ _subscriber(value);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
LastValue = new WeakReference(notification.Value);
|
|
|
+
|
|
|
if (Next != null)
|
|
|
{
|
|
|
Next.Target = new WeakReference(notification.Value);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (Next == null || notification.Error != null)
|
|
|
{
|
|
|
- _observer.OnNext(value);
|
|
|
+ _subscriber(value);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private void StartListening()
|
|
|
+ {
|
|
|
+ var target = _target.Target;
|
|
|
+
|
|
|
+ if (target == null)
|
|
|
+ {
|
|
|
+ ValueChanged(TargetNullNotification());
|
|
|
+ _listening = false;
|
|
|
+ }
|
|
|
+ else if (target != AvaloniaProperty.UnsetValue)
|
|
|
+ {
|
|
|
+ StartListeningCore(_target);
|
|
|
+ _listening = true;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _listening = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void StopListening()
|
|
|
+ {
|
|
|
+ StopListeningCore();
|
|
|
+ }
|
|
|
+
|
|
|
private BindingNotification TargetNullNotification()
|
|
|
{
|
|
|
return new BindingNotification(
|