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:43:30 +01:00
2025-02-16 11:54:23 +01:00
CheckReentrancy ( ) ;
2025-02-16 11:43:30 +01:00
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 ( ) ;
2024-11-10 23:28:25 +01:00
2025-02-16 11:54:23 +01:00
foreach ( var item in items )
Items . Insert ( 0 , item ) ;
2024-11-10 23:28:25 +01:00
2025-02-16 11:54:23 +01:00
OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Reset ) ) ;
}
2024-11-10 23:28:25 +01:00
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
}
}