// Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; using Avalonia.Utilities; namespace Avalonia.Collections { public static class NotifyCollectionChangedExtensions { /// /// Gets a weak observable for the CollectionChanged event. /// /// The collection. /// An observable. public static IObservable GetWeakCollectionChangedObservable( this INotifyCollectionChanged collection) { Contract.Requires(collection != null); return new WeakCollectionChangedObservable(new WeakReference(collection)); } /// /// Subcribes to the CollectionChanged event using a weak subscription. /// /// The collection. /// /// An action called when the collection event is raised. /// /// A disposable used to terminate the subscription. public static IDisposable WeakSubscribe( this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler) { Contract.Requires(collection != null); Contract.Requires(handler != null); return collection.GetWeakCollectionChangedObservable() .Subscribe(e => handler.Invoke(collection, e)); } /// /// Subcribes to the CollectionChanged event using a weak subscription. /// /// The collection. /// /// An action called when the collection event is raised. /// /// A disposable used to terminate the subscription. public static IDisposable WeakSubscribe( this INotifyCollectionChanged collection, Action handler) { Contract.Requires(collection != null); Contract.Requires(handler != null); return collection.GetWeakCollectionChangedObservable() .Subscribe(handler); } private class WeakCollectionChangedObservable : ObservableBase, IWeakSubscriber { private WeakReference _sourceReference; private readonly Subject _changed = new Subject(); private int _count; public WeakCollectionChangedObservable(WeakReference source) { _sourceReference = source; } public void OnEvent(object sender, NotifyCollectionChangedEventArgs e) { _changed.OnNext(e); } protected override IDisposable SubscribeCore(IObserver observer) { if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance)) { if (_count++ == 0) { WeakSubscriptionManager.Subscribe( instance, nameof(instance.CollectionChanged), this); } return Observable.Using(() => Disposable.Create(DecrementCount), _ => _changed) .Subscribe(observer); } else { _changed.OnCompleted(); observer.OnCompleted(); return Disposable.Empty; } } private void DecrementCount() { if (--_count == 0) { if (_sourceReference.TryGetTarget(out INotifyCollectionChanged instance)) { WeakSubscriptionManager.Unsubscribe( instance, nameof(instance.CollectionChanged), this); } } } } } }