diff --git a/Torch/Extensions/ICollectionExtensions.cs b/Torch/Extensions/ICollectionExtensions.cs index d9a7abd..2e6842a 100644 --- a/Torch/Extensions/ICollectionExtensions.cs +++ b/Torch/Extensions/ICollectionExtensions.cs @@ -30,6 +30,20 @@ namespace Torch return source as IReadOnlyList ?? new ReadOnlyCollection(source); } + /// + /// Returns a read-only wrapped and proxies its and events. + /// + public static IReadOnlyList AsReadOnlyObservable(this IList source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + if (source is INotifyPropertyChanged && source is INotifyCollectionChanged) + return new ObservableReadOnlyList(source); + + throw new InvalidOperationException("The given list is not observable."); + } + /// /// Returns a read-only wrapped /// @@ -40,14 +54,68 @@ namespace Torch return source as IReadOnlyDictionary ?? new ReadOnlyDictionary(source); } + /// + /// Returns a read-only wrapped and proxies its and events. + /// public static IReadOnlyDictionary AsReadOnlyObservable(this IDictionary source) { if (source == null) throw new ArgumentNullException(nameof(source)); - return new ObservableReadOnlyDictionary(source); + + if (source is INotifyPropertyChanged && source is INotifyCollectionChanged) + return new ObservableReadOnlyDictionary(source); + + throw new InvalidOperationException("The given dictionary is not observable."); } - sealed class ObservableReadOnlyDictionary : ViewModel, IReadOnlyDictionary + sealed class ObservableReadOnlyList : ViewModel, IReadOnlyList, IDisposable + { + private IList _list; + + public ObservableReadOnlyList(IList list) + { + _list = list; + + if (_list is INotifyPropertyChanged p) + p.PropertyChanged += OnPropertyChanged; + + if (_list is INotifyCollectionChanged c) + c.CollectionChanged += OnCollectionChanged; + } + + public void Dispose() + { + if (_list is INotifyPropertyChanged p) + p.PropertyChanged -= OnPropertyChanged; + + if (_list is INotifyCollectionChanged c) + c.CollectionChanged -= OnCollectionChanged; + } + + private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + OnCollectionChanged(e); + } + + private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + OnPropertyChanged(e.PropertyName); + } + + /// + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_list).GetEnumerator(); + + /// + public int Count => _list.Count; + + /// + public T this[int index] => _list[index]; + } + + sealed class ObservableReadOnlyDictionary : ViewModel, IReadOnlyDictionary, IDisposable { private readonly IDictionary _dictionary; @@ -62,6 +130,15 @@ namespace Torch c.CollectionChanged += OnCollectionChanged; } + public void Dispose() + { + if (_dictionary is INotifyPropertyChanged p) + p.PropertyChanged -= OnPropertyChanged; + + if (_dictionary is INotifyCollectionChanged c) + c.CollectionChanged -= OnCollectionChanged; + } + private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { OnCollectionChanged(e); diff --git a/Torch/Plugins/PluginManager.cs b/Torch/Plugins/PluginManager.cs index cf2e724..152ebe9 100644 --- a/Torch/Plugins/PluginManager.cs +++ b/Torch/Plugins/PluginManager.cs @@ -34,7 +34,7 @@ namespace Torch.Managers #pragma warning restore 649 /// - public IReadOnlyDictionary Plugins => _plugins.AsReadOnly(); + public IReadOnlyDictionary Plugins => _plugins.AsReadOnlyObservable(); public event Action> PluginsLoaded;