Created
October 21, 2020 21:07
-
-
Save KirillAshikhmin/fc31e174b668566cc9e60317379f1fdc to your computer and use it in GitHub Desktop.
Extended ObservableCollectionwith range actions and corrent notify calls. Use *WithNotify methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.Collections.Specialized; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using JetBrains.Annotations; | |
// ReSharper disable PossibleMultipleEnumeration | |
// ReSharper disable UnusedMember.Global | |
// ReSharper disable LocalizableElement | |
namespace KirillAshikhmin.Helpers | |
{ | |
public class SmartObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged | |
{ | |
readonly object _lockObject = new object(); | |
readonly ConcurrentDictionary<string, object> _properties = new ConcurrentDictionary<string, object>(); | |
/// <summary> | |
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. | |
/// </summary> | |
public SmartObservableCollection() { } | |
/// <summary> | |
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. | |
/// </summary> | |
/// <param name="collection">collection: The collection from which the elements are copied.</param> | |
/// <exception cref="System.ArgumentNullException">The collection parameter cannot be null.</exception> | |
public SmartObservableCollection(IList<T> collection) : base(collection) { } | |
public SmartObservableCollection(IEnumerable<T> collection) : base(collection.ToList()) { } | |
protected bool CallPropertyChangeEvent { get; set; } = true; | |
public void AddWithNotify(T item) | |
{ | |
lock (_lockObject) | |
{ | |
var index = Count; | |
Add(item); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T> {item}, index)); | |
} | |
} | |
public void InsertWithNotify(int index, T item) | |
{ | |
lock (_lockObject) | |
{ | |
Insert(index, item); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T> {item}, index)); | |
} | |
} | |
public void RemoveWithNotify(T item) | |
{ | |
lock (_lockObject) | |
{ | |
var index = IndexOf(item); | |
if (index < 0) return; | |
Remove(item); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T> {item}, index); | |
OnCollectionChanged(args); | |
} | |
} | |
public void RemoveAtWithNotify(int at) | |
{ | |
lock (_lockObject) | |
{ | |
if (Count - 1 < at) return; | |
var item = this[at]; | |
Remove(item); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new List<T> {item}, at); | |
OnCollectionChanged(args); | |
} | |
} | |
public void ClearWithNotify() | |
{ | |
lock (_lockObject) | |
{ | |
Clear(); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); | |
} | |
} | |
/// <summary> | |
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T). | |
/// </summary> | |
public void AddRange(IEnumerable<T> collection) | |
{ | |
if (collection == null) throw new ArgumentNullException(nameof(collection)); | |
lock (_lockObject) | |
{ | |
var index = Count; | |
foreach (var i in collection) Add(i); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList(), index)); | |
} | |
} | |
/// <summary> | |
/// Adds the elements of the specified collection to the index of the ObservableCollection(Of T). | |
/// </summary> | |
public void InsertRange(int index, IEnumerable<T> collection) | |
{ | |
if (collection == null) return; | |
lock (_lockObject) | |
{ | |
var newItems = collection as T[] ?? collection.ToArray(); | |
if (newItems.Length == 0) return; | |
var newIndex = index; | |
foreach (var i in newItems) | |
{ | |
Insert(newIndex++, i); | |
} | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newItems, index)); | |
} | |
} | |
/// <summary> | |
/// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). | |
/// </summary> | |
public void RemoveRange(IEnumerable<T> collection) | |
{ | |
lock (_lockObject) | |
{ | |
var oldItems = collection as T[] ?? collection.ToArray(); | |
if (oldItems.Length == 0) return; | |
var start = Items.IndexOf(oldItems.First()); | |
foreach (var i in oldItems) Items.Remove(i); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, start); | |
OnCollectionChanged(args); | |
} | |
} | |
public void Notify() | |
{ | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); | |
OnCollectionChanged(args); | |
} | |
/// <summary> | |
/// Clears the current collection and replaces it with the specified item. | |
/// </summary> | |
public void Replace(T item, T newItem) | |
{ | |
lock (_lockObject) | |
{ | |
var index = Items.IndexOf(item); | |
Items.Remove(item); | |
Items.Insert(index, newItem); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
var items = new List<T> {item}; | |
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, items, items, index); | |
OnCollectionChanged(args); | |
} | |
} | |
/// <summary> | |
/// Clears the current collection and replaces it with the specified collection. | |
/// </summary> | |
public void ReplaceRange(IEnumerable<T> collection) | |
{ | |
if (collection == null) throw new ArgumentNullException(nameof(collection)); | |
lock (_lockObject) | |
{ | |
Items.Clear(); | |
foreach (var i in collection) Items.Add(i); | |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); | |
} | |
} | |
public void RemoveAll(Func<T, bool> match) | |
{ | |
lock (_lockObject) | |
{ | |
var oldItems = this as T[] ?? this.ToArray(); | |
if (oldItems.Length == 0) return; | |
var start = Items.IndexOf(oldItems.First()); | |
foreach (var i in oldItems) | |
{ | |
if (match(i)) | |
{ | |
Items.Remove(i); | |
OnPropertyChanged(new PropertyChangedEventArgs("Count")); | |
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); | |
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldItems, start); | |
OnCollectionChanged(args); | |
} | |
} | |
} | |
} | |
public event NotifyCollectionChangedEventHandler CollectionChanged; | |
public event PropertyChangedEventHandler PropertyChanged; | |
public event PropertyChangingEventHandler PropertyChanging; | |
[NotifyPropertyChangedInvocator] | |
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |
protected void OnPropertyChanging([CallerMemberName] string propertyName = null) => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); | |
public void NotifyOfPropertyChanged(string propertyName) => OnPropertyChanged(propertyName); | |
protected void OnPropertyChanged(PropertyChangedEventArgs prop) => PropertyChanged?.Invoke(this, prop); | |
void OnCollectionChanged(NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) => CollectionChanged?.Invoke(this, notifyCollectionChangedEventArgs); | |
internal TProp Get<TProp>(TProp defValue = default, [CallerMemberName] string name = null) | |
{ | |
if (string.IsNullOrEmpty(name)) return defValue; | |
if (_properties.TryGetValue(name, out var value)) | |
return (TProp) value; | |
_properties.AddOrUpdate(name, defValue, (s, o) => defValue); | |
return defValue; | |
} | |
internal bool Set(object value, [CallerMemberName] string name = null) | |
{ | |
if (string.IsNullOrEmpty(name)) | |
return false; | |
var isExists = _properties.TryGetValue(name, out var getValue); | |
if (isExists && Equals(value, getValue)) | |
return false; | |
if (CallPropertyChangeEvent) | |
OnPropertyChanging(name); | |
_properties.AddOrUpdate(name, value, (s, o) => value); | |
if (CallPropertyChangeEvent) | |
OnPropertyChanged(name); | |
return true; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment