Refactor property changed implementations

This commit is contained in:
John Gross
2017-10-02 23:44:06 -07:00
parent 6f650c8bbd
commit 4f1a03811a
4 changed files with 89 additions and 61 deletions

View File

@@ -12,7 +12,7 @@ using System.Windows.Threading;
namespace Torch.Collections namespace Torch.Collections
{ {
[Serializable] [Serializable]
public class ObservableDictionary<TKey, TValue> : ViewModel, IDictionary<TKey, TValue>, INotifyCollectionChanged public class ObservableDictionary<TKey, TValue> : ViewModel, IDictionary<TKey, TValue>
{ {
private IDictionary<TKey, TValue> _internalDict; private IDictionary<TKey, TValue> _internalDict;
@@ -37,12 +37,6 @@ namespace Torch.Collections
}; };
} }
/// <inheritdoc />
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <inheritdoc />
public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc /> /// <inheritdoc />
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{ {
@@ -153,26 +147,5 @@ namespace Torch.Collections
/// <inheritdoc /> /// <inheritdoc />
public ICollection<TValue> Values => _internalDict.Values; public ICollection<TValue> 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);
}
}
} }
} }

View File

@@ -12,16 +12,10 @@ namespace Torch
/// <summary> /// <summary>
/// An observable version of <see cref="List{T}"/>. /// An observable version of <see cref="List{T}"/>.
/// </summary> /// </summary>
public class ObservableList<T> : IList<T>, INotifyCollectionChanged, INotifyPropertyChanged public class ObservableList<T> : ViewModel, IList<T>
{ {
private List<T> _internalList = new List<T>(); private List<T> _internalList = new List<T>();
/// <inheritdoc />
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <inheritdoc />
public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc /> /// <inheritdoc />
public void Clear() 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));
}
/// <inheritdoc /> /// <inheritdoc />
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {

View File

@@ -2,6 +2,9 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Threading;
namespace Torch namespace Torch
{ {
@@ -37,6 +40,63 @@ namespace Torch
return source as IReadOnlyDictionary<TKey, TValue> ?? new ReadOnlyDictionary<TKey, TValue>(source); return source as IReadOnlyDictionary<TKey, TValue> ?? new ReadOnlyDictionary<TKey, TValue>(source);
} }
public static IReadOnlyDictionary<TKey, TValue> AsReadOnlyObservable<TKey, TValue>(this IDictionary<TKey, TValue> source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
return new ObservableReadOnlyDictionary<TKey, TValue>(source);
}
sealed class ObservableReadOnlyDictionary<TKey, TValue> : ViewModel, IReadOnlyDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _dictionary;
public ObservableReadOnlyDictionary(IDictionary<TKey, TValue> 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);
}
/// <inheritdoc />
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _dictionary.GetEnumerator();
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_dictionary).GetEnumerator();
/// <inheritdoc />
public int Count => _dictionary.Count;
/// <inheritdoc />
public bool ContainsKey(TKey key) => _dictionary.ContainsKey(key);
/// <inheritdoc />
public bool TryGetValue(TKey key, out TValue value) => _dictionary.TryGetValue(key, out value);
/// <inheritdoc />
public TValue this[TKey key] => _dictionary[key];
/// <inheritdoc />
public IEnumerable<TKey> Keys => _dictionary.Keys;
/// <inheritdoc />
public IEnumerable<TValue> Values => _dictionary.Values;
}
sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T> sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
{ {
private readonly ICollection<T> _source; private readonly ICollection<T> _source;

View File

@@ -7,21 +7,47 @@ using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Threading;
namespace Torch namespace Torch
{ {
/// <summary> /// <summary>
/// Provides a method to notify an observer of changes to an object's properties. /// Provides a method to notify an observer of changes to an object's properties.
/// </summary> /// </summary>
public abstract class ViewModel : INotifyPropertyChanged public abstract class ViewModel : INotifyPropertyChanged, INotifyCollectionChanged
{ {
/// <inheritdoc />
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc />
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propName = "") protected virtual void OnPropertyChanged([CallerMemberName] string propName = "")
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(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<T>(ref T backingField, T value, [CallerMemberName] string propName = "") protected virtual void SetValue<T>(ref T backingField, T value, [CallerMemberName] string propName = "")
{ {
if (backingField.Equals(value)) if (backingField.Equals(value))