// 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);
}
}
}
}
}
}