diff --git a/src/Avalonia.Base/Reactive/LightweightObservableBase.cs b/src/Avalonia.Base/Reactive/LightweightObservableBase.cs
new file mode 100644
index 0000000000..3ae6e5916b
--- /dev/null
+++ b/src/Avalonia.Base/Reactive/LightweightObservableBase.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Reactive;
+using System.Reactive.Disposables;
+using Avalonia.Threading;
+
+namespace Avalonia.Reactive
+{
+ ///
+ /// Lightweight base class for observable implementations.
+ ///
+ /// The observable type.
+ ///
+ /// is rather heavyweight in terms of allocations and memory
+ /// usage. This class provides a more lightweight base for some internal observable types
+ /// in the Avalonia framework.
+ ///
+ public abstract class LightweightObservableBase : IObservable
+ {
+ private Exception _error;
+ private List> _observers = new List>();
+
+ public IDisposable Subscribe(IObserver observer)
+ {
+ Contract.Requires(observer != null);
+ Dispatcher.UIThread.VerifyAccess();
+
+ if (_observers == null)
+ {
+ if (_error != null)
+ {
+ observer.OnError(_error);
+ }
+ else
+ {
+ observer.OnCompleted();
+ }
+
+ return Disposable.Empty;
+ }
+
+ lock (_observers)
+ {
+ _observers.Add(observer);
+ }
+
+ if (_observers.Count == 1)
+ {
+ Initialize();
+ }
+
+ Subscribed(observer);
+
+ return Disposable.Create(() =>
+ {
+ _observers?.Remove(observer);
+
+ if (_observers?.Count == 0)
+ {
+ Deinitialize();
+ _observers.TrimExcess();
+ }
+ });
+ }
+
+ protected abstract void Initialize();
+ protected abstract void Deinitialize();
+
+ protected void PublishNext(T value)
+ {
+ lock (_observers)
+ {
+ foreach (var observer in _observers)
+ {
+ observer.OnNext(value);
+ }
+ }
+ }
+
+ protected void PublishCompleted()
+ {
+ lock (_observers)
+ {
+ foreach (var observer in _observers)
+ {
+ observer.OnCompleted();
+ }
+
+ _observers = null;
+ }
+
+ Deinitialize();
+ }
+
+ protected void PublishError(Exception error)
+ {
+ lock (_observers)
+ {
+ foreach (var observer in _observers)
+ {
+ observer.OnError(error);
+ }
+
+ _observers = null;
+ }
+
+ _error = error;
+ Deinitialize();
+ }
+
+ protected virtual void Subscribed(IObserver observer)
+ {
+ }
+ }
+}
diff --git a/src/Avalonia.Styling/Styling/ActivatedObservable.cs b/src/Avalonia.Styling/Styling/ActivatedObservable.cs
index 4933daed31..5b2774943a 100644
--- a/src/Avalonia.Styling/Styling/ActivatedObservable.cs
+++ b/src/Avalonia.Styling/Styling/ActivatedObservable.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Styling
/// An observable which is switched on or off according to an activator observable.
///
///
- /// An has two inputs: an activator observable a
+ /// An has two inputs: an activator observable and a
/// observable which produces the activated value. When the activator
/// produces true, the will produce the current activated
/// value. When the activator produces false it will produce
@@ -69,8 +69,8 @@ namespace Avalonia.Styling
}
protected new ActivatedObservable Parent => (ActivatedObservable)base.Parent;
- void IObserver
- internal class ActivatedValue : IObservable, IDescription
+ internal class ActivatedValue : LightweightObservableBase, IDescription
{
private static readonly object NotSent = new object();
- private List> _observers = new List>();
private IDisposable _activatorSubscription;
private object _value;
private object _last = NotSent;
@@ -74,68 +71,35 @@ namespace Avalonia.Styling
protected ActivatorListener Listener { get; }
- public virtual IDisposable Subscribe(IObserver observer)
+ protected virtual void ActiveChanged(bool active)
{
- Contract.Requires(observer != null);
- Dispatcher.UIThread.VerifyAccess();
-
- _observers.Add(observer);
-
- if (_observers.Count == 1)
- {
- Initialize();
- }
-
- return Disposable.Create(() =>
- {
- _observers.Remove(observer);
-
- if (_observers.Count == 0)
- {
- Deinitialize();
- }
- });
+ IsActive = active;
+ PublishValue();
}
+ protected virtual void CompletedReceived() => PublishCompleted();
+
protected virtual ActivatorListener CreateListener() => new ActivatorListener(this);
- protected virtual void Deinitialize()
+ protected override void Deinitialize()
{
_activatorSubscription.Dispose();
_activatorSubscription = null;
}
- protected virtual void Initialize()
- {
- _activatorSubscription = Activator.Subscribe(Listener);
- }
+ protected virtual void ErrorReceived(Exception error) => PublishError(error);
- protected virtual void NotifyCompleted()
+ protected override void Initialize()
{
- foreach (var observer in _observers)
- {
- observer.OnCompleted();
- }
-
- Deinitialize();
- _observers = null;
+ _activatorSubscription = Activator.Subscribe(Listener);
}
- protected virtual void NotifyError(Exception error)
+ protected override void Subscribed(IObserver observer)
{
- foreach (var observer in _observers)
+ if (IsActive == true)
{
- observer.OnError(error);
+ observer.OnNext(Value);
}
-
- Deinitialize();
- _observers = null;
- }
-
- protected virtual void NotifyActive(bool active)
- {
- IsActive = active;
- PublishValue();
}
private void PublishValue()
@@ -146,11 +110,7 @@ namespace Avalonia.Styling
if (!Equals(v, _last))
{
- foreach (var observer in _observers)
- {
- observer.OnNext(v);
- }
-
+ PublishNext(v);
_last = v;
}
}
@@ -165,9 +125,9 @@ namespace Avalonia.Styling
protected ActivatedValue Parent { get; }
- void IObserver.OnCompleted() => Parent.NotifyCompleted();
- void IObserver.OnError(Exception error) => Parent.NotifyError(error);
- void IObserver.OnNext(bool value) => Parent.NotifyActive(value);
+ void IObserver.OnCompleted() => Parent.CompletedReceived();
+ void IObserver.OnError(Exception error) => Parent.ErrorReceived(error);
+ void IObserver.OnNext(bool value) => Parent.ActiveChanged(value);
}
}
}