From 4f1a03811ad56754c45e4390a70dcd9bb37ee3c6 Mon Sep 17 00:00:00 2001 From: John Gross Date: Mon, 2 Oct 2017 23:44:06 -0700 Subject: [PATCH] Refactor property changed implementations --- Torch/Collections/ObservableDictionary.cs | 29 +---------- Torch/Collections/ObservableList.cs | 33 +------------ Torch/Extensions/ICollectionExtensions.cs | 60 +++++++++++++++++++++++ Torch/ViewModels/ViewModel.cs | 28 ++++++++++- 4 files changed, 89 insertions(+), 61 deletions(-) diff --git a/Torch/Collections/ObservableDictionary.cs b/Torch/Collections/ObservableDictionary.cs index 68a4a33..0b0a3ea 100644 --- a/Torch/Collections/ObservableDictionary.cs +++ b/Torch/Collections/ObservableDictionary.cs @@ -12,7 +12,7 @@ using System.Windows.Threading; namespace Torch.Collections { [Serializable] - public class ObservableDictionary : ViewModel, IDictionary, INotifyCollectionChanged + public class ObservableDictionary : ViewModel, IDictionary { private IDictionary _internalDict; @@ -37,12 +37,6 @@ namespace Torch.Collections }; } - /// - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// - public event PropertyChangedEventHandler PropertyChanged; - /// public IEnumerator> GetEnumerator() { @@ -153,26 +147,5 @@ namespace Torch.Collections /// public ICollection Values => _internalDict.Values; - - private void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged; - if (collectionChanged != null) - foreach (NotifyCollectionChangedEventHandler nh in collectionChanged.GetInvocationList()) - { - var dispObj = nh.Target as DispatcherObject; - - var dispatcher = dispObj?.Dispatcher; - if (dispatcher != null && !dispatcher.CheckAccess()) - { - dispatcher.BeginInvoke( - (Action)(() => nh.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))), - DispatcherPriority.DataBind); - continue; - } - - nh.Invoke(this, e); - } - } } } diff --git a/Torch/Collections/ObservableList.cs b/Torch/Collections/ObservableList.cs index a4350e8..bb8e480 100644 --- a/Torch/Collections/ObservableList.cs +++ b/Torch/Collections/ObservableList.cs @@ -12,16 +12,10 @@ namespace Torch /// /// An observable version of . /// - public class ObservableList : IList, INotifyCollectionChanged, INotifyPropertyChanged + public class ObservableList : ViewModel, IList { private List _internalList = new List(); - /// - public event NotifyCollectionChangedEventHandler CollectionChanged; - - /// - public event PropertyChangedEventHandler PropertyChanged; - /// public void Clear() { @@ -146,31 +140,6 @@ namespace Torch } } - protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) - { - var collectionChanged = CollectionChanged; - if (collectionChanged != null) - foreach (var del in collectionChanged.GetInvocationList()) - { - var nh = (NotifyCollectionChangedEventHandler)del; - var dispObj = nh.Target as DispatcherObject; - - var dispatcher = dispObj?.Dispatcher; - if (dispatcher != null && !dispatcher.CheckAccess()) - { - dispatcher.BeginInvoke(() => nh.Invoke(this, e), DispatcherPriority.DataBind); - continue; - } - - nh.Invoke(this, e); - } - } - - protected void OnPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - /// public IEnumerator GetEnumerator() { diff --git a/Torch/Extensions/ICollectionExtensions.cs b/Torch/Extensions/ICollectionExtensions.cs index acc952c..d9a7abd 100644 --- a/Torch/Extensions/ICollectionExtensions.cs +++ b/Torch/Extensions/ICollectionExtensions.cs @@ -2,6 +2,9 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows.Threading; namespace Torch { @@ -37,6 +40,63 @@ namespace Torch return source as IReadOnlyDictionary ?? new ReadOnlyDictionary(source); } + public static IReadOnlyDictionary AsReadOnlyObservable(this IDictionary source) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + return new ObservableReadOnlyDictionary(source); + } + + sealed class ObservableReadOnlyDictionary : ViewModel, IReadOnlyDictionary + { + private readonly IDictionary _dictionary; + + public ObservableReadOnlyDictionary(IDictionary dictionary) + { + _dictionary = dictionary; + + 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); + } + + private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + OnPropertyChanged(e.PropertyName); + } + + /// + public IEnumerator> GetEnumerator() => _dictionary.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_dictionary).GetEnumerator(); + + /// + public int Count => _dictionary.Count; + + /// + public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key); + + /// + public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value); + + /// + public TValue this[TKey key] => _dictionary[key]; + + /// + public IEnumerable Keys => _dictionary.Keys; + + /// + public IEnumerable Values => _dictionary.Values; + } + sealed class ReadOnlyCollectionAdapter : IReadOnlyCollection { private readonly ICollection _source; diff --git a/Torch/ViewModels/ViewModel.cs b/Torch/ViewModels/ViewModel.cs index 238b103..cbd3581 100644 --- a/Torch/ViewModels/ViewModel.cs +++ b/Torch/ViewModels/ViewModel.cs @@ -7,21 +7,47 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using System.Windows.Threading; namespace Torch { /// /// Provides a method to notify an observer of changes to an object's properties. /// - public abstract class ViewModel : INotifyPropertyChanged + public abstract class ViewModel : INotifyPropertyChanged, INotifyCollectionChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + public event NotifyCollectionChangedEventHandler CollectionChanged; + protected virtual void OnPropertyChanged([CallerMemberName] string propName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + NotifyCollectionChangedEventHandler collectionChanged = CollectionChanged; + if (collectionChanged != null) + foreach (NotifyCollectionChangedEventHandler nh in collectionChanged.GetInvocationList()) + { + var dispObj = nh.Target as DispatcherObject; + + var dispatcher = dispObj?.Dispatcher; + if (dispatcher != null && !dispatcher.CheckAccess()) + { + dispatcher.BeginInvoke( + (Action)(() => nh.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))), + DispatcherPriority.DataBind); + continue; + } + + nh.Invoke(this, e); + } + } + protected virtual void SetValue(ref T backingField, T value, [CallerMemberName] string propName = "") { if (backingField.Equals(value))