Skip to content

Commit

Permalink
Added support for WPF Range operations
Browse files Browse the repository at this point in the history
  • Loading branch information
PieterjanDC2 committed Jun 27, 2020
1 parent 988c25a commit 3c4a0ea
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
<Description>Extended version of System.Collections.ObjectModel.ObservableCollection. This class allows you to:
1) Use AddRange, invoking the CollectionChanged event only once
2) Use RemoveRange, invoking the CollectionChanged event only once
3) Monitor properties of the items in the collection</Description>
3) Monitor properties of the items in the collection
4) Support for WPF</Description>
<Company>MintPlayer</Company>
<Authors>Pieterjan De Clippel</Authors>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/MintPlayer/ObservableCollection</PackageProjectUrl>
<RepositoryUrl>https://github.com/MintPlayer/ObservableCollection</RepositoryUrl>
<PackageProjectUrl>https://github.com/MintPlayer/MintPlayer.ObservableCollection</PackageProjectUrl>
<RepositoryUrl>https://github.com/MintPlayer/MintPlayer.ObservableCollection</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>ObservableCollection, AddRange, RemoveRange, ItemPropertyChanged</PackageTags>
<PackageId>MintPlayer.ObservableCollection</PackageId>
Expand Down
89 changes: 82 additions & 7 deletions MintPlayer.ObservableCollection/ObservableCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System.Collections.Specialized;
using MintPlayer.ObservableCollection.Events.EventHandlers;
using System.ComponentModel;
using System.Reflection;
using System.Diagnostics;

namespace MintPlayer.ObservableCollection
{
Expand All @@ -23,6 +25,7 @@ public ObservableCollection(IEnumerable<T> items) : this()
#region Private fields

private bool isAddingOrRemovingRange = false;
[NonSerialized] private DeferredEventsCollection _deferredEvents;

#endregion

Expand Down Expand Up @@ -57,33 +60,68 @@ public void RemoveRange(IEnumerable<T> items)
}
#endregion

private bool IsCollectionView(object target)
{
#region Build a type tree
var typeTree = new List<Type>();
var curType = target.GetType();
while (curType != null)
{
typeTree.Add(curType);
curType = curType.BaseType;
}
#endregion

#region Check if any of the types matches the CollectionView
return typeTree.Any(t => t.FullName == "System.Windows.Data.CollectionView");
#endregion
}

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!isAddingOrRemovingRange)
{
// Only invoke the CollectionChanged with all items, not individually
base.OnCollectionChanged(e);
var _deferredEv = (ICollection<NotifyCollectionChangedEventArgs>)_deferredEvents;
if (_deferredEv == null)
{
foreach (var handler in GetHandlers())
{
var isCollectionView = IsCollectionView(handler.Target);
if (IsRange(e) && isCollectionView)
{
// Call the Refresh method if the target is a WPF CollectionView
handler.Target.GetType().GetMethod("Refresh").Invoke(handler.Target, new object[0]);
}
else
{
handler(this, e);
}
}
}
else
{
_deferredEv.Add(e);
}

// Also only attach the PropertyChanged event handler when we're not into
// the process of adding a number of items one by one.
var itf = typeof(T).GetInterfaces();

if (itf.Contains(typeof(System.ComponentModel.INotifyPropertyChanged)))

if (typeof(T).GetInterfaces().Contains(typeof(INotifyPropertyChanged)))
{
// First detach all event handlers
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
((System.ComponentModel.INotifyPropertyChanged)item).PropertyChanged -= ObservableCollection_Item_PropertyChanged;
((INotifyPropertyChanged)item).PropertyChanged -= ObservableCollection_Item_PropertyChanged;
}
}
// Then attach all event handlers
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
((System.ComponentModel.INotifyPropertyChanged)item).PropertyChanged += ObservableCollection_Item_PropertyChanged;
((INotifyPropertyChanged)item).PropertyChanged += ObservableCollection_Item_PropertyChanged;
}
}
}
Expand Down Expand Up @@ -117,6 +155,43 @@ private void InternalAddRange(IEnumerable<T> items)
isAddingOrRemovingRange = false;
}
}

private bool IsRange(NotifyCollectionChangedEventArgs e)
{
return e.NewItems?.Count > 1 || e.OldItems?.Count > 1;
}

private IEnumerable<NotifyCollectionChangedEventHandler> GetHandlers()
{
var info = typeof(System.Collections.ObjectModel.ObservableCollection<T>).GetField(nameof(CollectionChanged), BindingFlags.Instance | BindingFlags.NonPublic);
var @event = (MulticastDelegate)info.GetValue(this);
return
@event?.GetInvocationList().Cast<NotifyCollectionChangedEventHandler>().Distinct()
?? Enumerable.Empty<NotifyCollectionChangedEventHandler>();
}

#endregion

#region Private Types
sealed class DeferredEventsCollection : List<NotifyCollectionChangedEventArgs>, IDisposable
{
readonly ObservableCollection<T> _collection;
public DeferredEventsCollection(ObservableCollection<T> collection)
{
Debug.Assert(collection != null);
Debug.Assert(collection._deferredEvents == null);
_collection = collection;
_collection._deferredEvents = this;
}

public void Dispose()
{
_collection._deferredEvents = null;
foreach (var args in this)
_collection.OnCollectionChanged(args);
}
}
#endregion Private Types

}
}

0 comments on commit 3c4a0ea

Please sign in to comment.