Files
Wino-Mail/Wino.Core.Domain/Collections/ObservableRangeCollection.cs

174 lines
6.0 KiB
C#
Raw Normal View History

2024-04-18 01:44:37 +02:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
2025-02-16 11:54:23 +01:00
namespace Wino.Core.Domain.Collections;
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableRangeCollection<T> : ObservableCollection<T>
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
2025-02-16 11:35:43 +01:00
/// <summary>
2025-02-16 11:54:23 +01:00
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
2025-02-16 11:35:43 +01:00
/// </summary>
2025-02-16 11:54:23 +01:00
public ObservableRangeCollection()
: base()
2025-02-16 11:35:43 +01:00
{
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <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="ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableRangeCollection(IEnumerable<T> collection)
: base(collection)
{
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Add)
{
if (notificationMode != NotifyCollectionChangedAction.Add && notificationMode != NotifyCollectionChangedAction.Reset)
throw new ArgumentException("Mode must be either Add or Reset for AddRange.", nameof(notificationMode));
if (collection == null)
throw new ArgumentNullException(nameof(collection));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
CheckReentrancy();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var startIndex = Count;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var itemsAdded = AddArrangeCore(collection);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (!itemsAdded)
return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (notificationMode == NotifyCollectionChangedAction.Reset)
{
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
return;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var changedItems = collection is List<T> ? (List<T>)collection : new List<T>(collection);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Add,
changedItems: changedItems,
startingIndex: startIndex);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). NOTE: with notificationMode = Remove, removed items starting index is not set because items are not guaranteed to be consecutive.
/// </summary>
public void RemoveRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Reset)
{
if (notificationMode != NotifyCollectionChangedAction.Remove && notificationMode != NotifyCollectionChangedAction.Reset)
throw new ArgumentException("Mode must be either Remove or Reset for RemoveRange.", nameof(notificationMode));
if (collection == null)
throw new ArgumentNullException(nameof(collection));
2025-02-16 11:54:23 +01:00
CheckReentrancy();
2025-02-16 11:54:23 +01:00
if (notificationMode == NotifyCollectionChangedAction.Reset)
{
var raiseEvents = false;
foreach (var item in collection)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
Items.Remove(item);
raiseEvents = true;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (raiseEvents)
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
return;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var changedItems = new List<T>(collection);
for (var i = 0; i < changedItems.Count; i++)
{
if (!Items.Remove(changedItems[i]))
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
changedItems.RemoveAt(i); //Can't use a foreach because changedItems is intended to be (carefully) modified
i--;
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (changedItems.Count == 0)
return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Remove,
changedItems: changedItems);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <summary>
/// Clears the current collection and replaces it with the specified item.
/// </summary>
public void Replace(T item) => ReplaceRange(new T[] { item });
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
/// <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));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
CheckReentrancy();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var previouslyEmpty = Items.Count == 0;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
Items.Clear();
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
AddArrangeCore(collection);
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
var currentlyEmpty = Items.Count == 0;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (previouslyEmpty && currentlyEmpty)
return;
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
public void InsertRange(IEnumerable<T> items)
{
CheckReentrancy();
2025-02-16 11:54:23 +01:00
foreach (var item in items)
Items.Insert(0, item);
2025-02-16 11:54:23 +01:00
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
2025-02-16 11:54:23 +01:00
private bool AddArrangeCore(IEnumerable<T> collection)
{
var itemAdded = false;
foreach (var item in collection)
2024-04-18 01:44:37 +02:00
{
2025-02-16 11:54:23 +01:00
Items.Add(item);
itemAdded = true;
2024-04-18 01:44:37 +02:00
}
2025-02-16 11:54:23 +01:00
return itemAdded;
}
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
private void RaiseChangeNotificationEvents(NotifyCollectionChangedAction action, List<T> changedItems = null, int startingIndex = -1)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
2024-04-18 01:44:37 +02:00
2025-02-16 11:54:23 +01:00
if (changedItems is null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action));
else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItems: changedItems, startingIndex: startingIndex));
2024-04-18 01:44:37 +02:00
}
}