I want to be able to add a range and get updated for the entire bulk.
I also want to be able to cancel the action before it\'s done (i.e. collection changing besides
Here's a modification of the accepted answer to provide more functionality.
RangeCollection.cs:
public class RangeCollection : ObservableCollection
{
#region Members
///
/// Occurs when a single item is added.
///
public event EventHandler> ItemAdded;
///
/// Occurs when a single item is inserted.
///
public event EventHandler> ItemInserted;
///
/// Occurs when a single item is removed.
///
public event EventHandler> ItemRemoved;
///
/// Occurs when a single item is replaced.
///
public event EventHandler> ItemReplaced;
///
/// Occurs when items are added to this.
///
public event EventHandler> ItemsAdded;
///
/// Occurs when items are removed from this.
///
public event EventHandler> ItemsRemoved;
///
/// Occurs when items are replaced within this.
///
public event EventHandler> ItemsReplaced;
///
/// Occurs when entire collection is cleared.
///
public event EventHandler> ItemsCleared;
///
/// Occurs when entire collection is replaced.
///
public event EventHandler> CollectionReplaced;
#endregion
#region Helper Methods
///
/// Throws exception if any of the specified objects are null.
///
private void Check(params T[] Items)
{
foreach (T Item in Items)
{
if (Item == null)
{
throw new ArgumentNullException("Item cannot be null.");
}
}
}
private void Check(IEnumerable Items)
{
if (Items == null) throw new ArgumentNullException("Items cannot be null.");
}
private void Check(IEnumerable> Items)
{
if (Items == null) throw new ArgumentNullException("Items cannot be null.");
}
private void RaiseChanged(NotifyCollectionChangedAction Action)
{
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
#endregion
#region Bulk Methods
///
/// Adds the elements of the specified collection to the end of this.
///
public void AddRange(IEnumerable NewItems)
{
this.Check(NewItems);
foreach (var i in NewItems) this.Items.Add(i);
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
this.OnItemsAdded(new ItemsAddedEventArgs(NewItems));
}
///
/// Adds variable IEnumerable to this.
///
///
public void AddRange(params IEnumerable[] NewItems)
{
this.Check(NewItems);
foreach (IEnumerable Items in NewItems) foreach (T Item in Items) this.Items.Add(Item);
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
//TO-DO: Raise OnItemsAdded with combined IEnumerable.
}
///
/// Removes the first occurence of each item in the specified collection.
///
public void Remove(IEnumerable OldItems)
{
this.Check(OldItems);
foreach (var i in OldItems) Items.Remove(i);
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
OnItemsRemoved(new ItemsRemovedEventArgs(OldItems));
}
///
/// Removes all occurences of each item in the specified collection.
///
///
public void RemoveAll(IEnumerable OldItems)
{
this.Check(OldItems);
var set = new HashSet(OldItems);
var list = this as List;
int i = 0;
while (i < this.Count) if (set.Contains(this[i])) this.RemoveAt(i); else i++;
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
OnItemsRemoved(new ItemsRemovedEventArgs(OldItems));
}
///
/// Replaces all occurences of a single item with specified item.
///
public void ReplaceAll(T Old, T New)
{
this.Check(Old, New);
this.Replace(Old, New, false);
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
this.OnItemReplaced(new ItemReplacedEventArgs(Old, New));
}
///
/// Clears this and adds specified collection.
///
public void ReplaceCollection(IEnumerable NewItems, bool SupressEvent = false)
{
this.Check(NewItems);
IEnumerable OldItems = new List(this.Items);
this.Items.Clear();
foreach (T Item in NewItems) this.Items.Add(Item);
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
this.OnReplaced(new CollectionReplacedEventArgs(OldItems, NewItems));
}
private void Replace(T Old, T New, bool BreakFirst)
{
List Cloned = new List(this.Items);
int i = 0;
foreach (T Item in Cloned)
{
if (Item.Equals(Old))
{
this.Items.Remove(Item);
this.Items.Insert(i, New);
if (BreakFirst) break;
}
i++;
}
}
///
/// Replaces the first occurence of a single item with specified item.
///
public void Replace(T Old, T New)
{
this.Check(Old, New);
this.Replace(Old, New, true);
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
this.OnItemReplaced(new ItemReplacedEventArgs(Old, New));
}
#endregion
#region New Methods
///
/// Removes a single item.
///
///
public new void Remove(T Item)
{
this.Check(Item);
base.Remove(Item);
OnItemRemoved(new ItemRemovedEventArgs(Item));
}
///
/// Removes a single item at specified index.
///
///
public new void RemoveAt(int i)
{
T OldItem = this.Items[i]; //This will throw first if null
base.RemoveAt(i);
OnItemRemoved(new ItemRemovedEventArgs(OldItem));
}
///
/// Clears this.
///
public new void Clear()
{
IEnumerable OldItems = new List(this.Items);
this.Items.Clear();
this.RaiseChanged(NotifyCollectionChangedAction.Reset);
this.OnCleared(new ItemsClearedEventArgs(OldItems));
}
///
/// Adds a single item to end of this.
///
///
public new void Add(T Item)
{
this.Check(Item);
base.Add(Item);
this.OnItemAdded(new ItemAddedEventArgs(Item));
}
///
/// Inserts a single item at specified index.
///
///
///
public new void Insert(int i, T Item)
{
this.Check(Item);
base.Insert(i, Item);
this.OnItemInserted(new ItemInsertedEventArgs(Item, i));
}
///
/// Returns list of T.ToString().
///
///
public new IEnumerable ToString()
{
foreach (T Item in this) yield return Item.ToString();
}
#endregion
#region Event Methods
private void OnItemAdded(ItemAddedEventArgs i)
{
if (this.ItemAdded != null) this.ItemAdded(this, new ItemAddedEventArgs(i.NewItem));
}
private void OnItemInserted(ItemInsertedEventArgs i)
{
if (this.ItemInserted != null) this.ItemInserted(this, new ItemInsertedEventArgs(i.NewItem, i.Index));
}
private void OnItemRemoved(ItemRemovedEventArgs i)
{
if (this.ItemRemoved != null) this.ItemRemoved(this, new ItemRemovedEventArgs(i.OldItem));
}
private void OnItemReplaced(ItemReplacedEventArgs i)
{
if (this.ItemReplaced != null) this.ItemReplaced(this, new ItemReplacedEventArgs(i.OldItem, i.NewItem));
}
private void OnItemsAdded(ItemsAddedEventArgs i)
{
if (this.ItemsAdded != null) this.ItemsAdded(this, new ItemsAddedEventArgs(i.NewItems));
}
private void OnItemsRemoved(ItemsRemovedEventArgs i)
{
if (this.ItemsRemoved != null) this.ItemsRemoved(this, new ItemsRemovedEventArgs(i.OldItems));
}
private void OnItemsReplaced(ItemsReplacedEventArgs i)
{
if (this.ItemsReplaced != null) this.ItemsReplaced(this, new ItemsReplacedEventArgs(i.OldItems, i.NewItems));
}
private void OnCleared(ItemsClearedEventArgs i)
{
if (this.ItemsCleared != null) this.ItemsCleared(this, new ItemsClearedEventArgs(i.OldItems));
}
private void OnReplaced(CollectionReplacedEventArgs i)
{
if (this.CollectionReplaced != null) this.CollectionReplaced(this, new CollectionReplacedEventArgs(i.OldItems, i.NewItems));
}
#endregion
#region RangeCollection
///
/// Initializes a new instance.
///
public RangeCollection() : base() { }
///
/// Initializes a new instance from specified enumerable.
///
public RangeCollection(IEnumerable Collection) : base(Collection) { }
///
/// Initializes a new instance from specified list.
///
public RangeCollection(List List) : base(List) { }
///
/// Initializes a new instance with variable T.
///
public RangeCollection(params T[] Items) : base()
{
this.AddRange(Items);
}
///
/// Initializes a new instance with variable enumerable.
///
public RangeCollection(params IEnumerable[] Items) : base()
{
this.AddRange(Items);
}
#endregion
}
Events Classes:
public class CollectionReplacedEventArgs : ReplacedEventArgs
{
public CollectionReplacedEventArgs(IEnumerable Old, IEnumerable New) : base(Old, New) { }
}
public class ItemAddedEventArgs : EventArgs
{
public T NewItem;
public ItemAddedEventArgs(T t)
{
this.NewItem = t;
}
}
public class ItemInsertedEventArgs : EventArgs
{
public int Index;
public T NewItem;
public ItemInsertedEventArgs(T t, int i)
{
this.NewItem = t;
this.Index = i;
}
}
public class ItemRemovedEventArgs : EventArgs
{
public T OldItem;
public ItemRemovedEventArgs(T t)
{
this.OldItem = t;
}
}
public class ItemReplacedEventArgs : EventArgs
{
public T OldItem;
public T NewItem;
public ItemReplacedEventArgs(T Old, T New)
{
this.OldItem = Old;
this.NewItem = New;
}
}
public class ItemsAddedEventArgs : EventArgs
{
public IEnumerable NewItems;
public ItemsAddedEventArgs(IEnumerable t)
{
this.NewItems = t;
}
}
public class ItemsClearedEventArgs : RemovedEventArgs
{
public ItemsClearedEventArgs(IEnumerable Old) : base(Old) { }
}
public class ItemsRemovedEventArgs : RemovedEventArgs
{
public ItemsRemovedEventArgs(IEnumerable Old) : base(Old) { }
}
public class ItemsReplacedEventArgs : ReplacedEventArgs
{
public ItemsReplacedEventArgs(IEnumerable Old, IEnumerable New) : base(Old, New) { }
}
public class RemovedEventArgs : EventArgs
{
public IEnumerable OldItems;
public RemovedEventArgs(IEnumerable Old)
{
this.OldItems = Old;
}
}
public class ReplacedEventArgs : EventArgs
{
public IEnumerable OldItems;
public IEnumerable NewItems;
public ReplacedEventArgs(IEnumerable Old, IEnumerable New)
{
this.OldItems = Old;
this.NewItems = New;
}
}
Note: I did not manually raise OnCollectionChanged
in the base methods because it appears only to be possible to create a CollectionChangedEventArgs
using the Reset
action. If you try to raise OnCollectionChanged
using Reset
for a single item change, your items control will appear to flicker, which is something you want to avoid.