Meziantou.Framework
Meziantou.Framework copied to clipboard
ConcurrentObservableCollection<T> should provide a way to defer events
When modifying the collection, it may be faster to do all the modifications and then raise the Reset event instead of raising lots a Add/Remove events
using (collection.DeferEvents())
{
collection.AddRange(toAdd);
collection.RemoveRange(toRemove);
}
Par contre si on fait un reset, je pense que ça fera un reset global de la vue wpf non? Du coup dans le cas où il y a que des add et des remove, on pourrait pas combiner ces events? un event de type Add et un event de type Remove?
What do you think of this design?
public class ConcurrentObservableCollection<T>
{
+ /// <summary>
+ /// Combine CollectionChanged events when possible (for instance, add/remove)
+ /// </summary>
+ public bool OptimizeEvents { get; set; }
+ /// <summary>
+ /// Begin a batch of operations on the collection. No CollectionChanged event is raised during the batch.
+ /// A Reset event is sent at the end of the batch.
+ /// </summary>
+ public IDisposable BeginBatch();
}
I like and understand the BeginBatch part, but I don't understand the OptimizeEvents one. Could you detail a bit?
- Is it used only in Batch mode?
- Does it mean when it's true it will try to combine events and not always send a reset event when possible? Also I think it could be really interesting to have AddRange and RemoveRange methods.
BeginBatch always send a Reset event. OptimizeEvents is another option, not linked to the batch. As events are dequeue on the Dispatcher thread, there can be multiple events (Add/Remove/Update). It can be possible to suppress some events such as Add/Update followed by Remove.
I don't know if it could be interesting (I guess things based on your comment :)) but we can change BeginBatch to take a parameter that indicates if you want to use a Reset or optimized Add/Remove events.
We can add AddRange
and RemoveRange
but the implementation is just calling Add/Remove for each items.
public class ConcurrentObservableCollection<T>
{
+ public void AddRange(params T[] items);
+ public void AddRange(IEnumerable<T> items);
+
+ public void RemoveRange(params T[] items);
+ public void RemoveRange(IEnumerable<T> items);
+
+ /// <summary>
+ /// Combine CollectionChanged events when possible (for instance, add/remove)
+ /// </summary>
+ public bool OptimizeEvents { get; set; }
+
+ /// <summary>
+ /// Begin a batch of operations on the collection. No CollectionChanged event is raised during the batch.
+ /// A Reset event is sent at the end of the batch.
+ /// </summary>
+ public IDisposable BeginBatch(BatchMode mode);
}
+public enum BatchMode
+{
+ Normal, // Not sure of this name
+ Optimized,
+ Reset,
+}
any news?
@LePtitDev started PR #42. However, he didn't finish it (there were a few opened conversations). Then, the PR was closed when I changed the default branch from master to main.
If you want you can submit a new PR and resolve the opened discussion.
Yeah, this would be very good to have. I got her by my #230 ticket. I find that most of the time if we are updating "observeablecollection" from another thread, it is usually because we have so many records to process. Unfortunately, I don't quite understand the code well enough to implement what you are proposing above. But in theory, it would be good if we could batch the events and then only have one Reset or Add. I will see if I can figure out the code as it does things beyond what I can do.
I actually got this to work quite well. I actually don't know how to contribute, so I will put the pieces in here I created. If you want to tell me how I can contribute, I am happy to do that. I anyone puts in a better way, please let me know. But this works really, really well for me. I am adding thousands of records every 400 ms and the UI is flawless.
IndexableCollection.cs I added this: public void AddRange(IEnumerable<T> newitems) { lock (_lock) { _observableCollection?.EnqueueAddRange(newitems); } }
DispatchedObserveableCollection.cs I added this:
internal void EnqueueAddRange(IEnumerable<T> items) { EnqueueEvent(PendingEvent.AddRange(items)); }
PendingEvent.cs I added this: public static PendingEvent<T> AddRange<T>(IEnumerable<T> items) => new PendingEvent<T>(PendingEventType.AddRange, items);
And then added this under ProcessPendingEvents case PendingEventType.AddRange: AddRange(pendingEvent.RangeItems); break;
PendingEventType.cs, I added this to enum after Add
AddRange,
PendingEvent'1.cs, I added highlighted text
internal readonly struct PendingEvent<T> { public PendingEvent(PendingEventType type) { Type = type; Item = default; Index = -1; Items = default; RangeItems = default; }
public PendingEvent(PendingEventType type, int index)
{
Type = type;
Item = default;
Index = index;
Items = default;
**RangeItems = default;**
}
public PendingEvent(PendingEventType type, T item)
{
Type = type;
Item = item;
Index = -1;
Items = default;
**RangeItems = default;**
}
public PendingEvent(PendingEventType type, T item, int index)
{
Type = type;
Item = item;
Index = index;
Items = default;
**RangeItems = default;**
}
public PendingEvent(PendingEventType type, ImmutableList<T> items)
{
Type = type;
Items = items;
Item = default;
Index = default;
**RangeItems = default;**
}
public PendingEvent(PendingEventType type, IEnumerable<T> items)
{
Type = type;
Items = default;
Item = default;
Index = default;
**RangeItems = items;**
}
public PendingEventType Type { get; }
public T Item { get; }
public int Index { get; }
public ImmutableList<T> Items { get; }
**public IEnumerable<T> RangeItems { get; }**
}
And finally, under ObserveableCollectionBase.cs, I added this part. Last line very important because it will only update the UI after everything is added.
protected void AddRange(IEnumerable<T> items) { if (items != null) { var index = Items.Count; foreach(var item in items) { Items.Add(item); index++; }
OnCountPropertyChanged();
OnIndexerPropertyChanged();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(items), index));
}
}
The one part I hadn't figured out with the code is I am successfully inserting these values to the DispatcherCollection, but I notice the ImmutableList isn't in sync with it and trying to find out where I call to sync them?
Nevermind, I completely forgot to also do _items.AddRange.
This issue is stale because it has been open for 60 days with no activity.
This issue was closed because it has been inactive for 14 days since being marked as stale.