From 5a33a2639d7d1fe8fe82bb037740bea27c9a775b Mon Sep 17 00:00:00 2001 From: Edward Miller Date: Fri, 26 Jan 2024 19:33:32 -0600 Subject: [PATCH 1/2] Execute notify collection changed events as a batch --- .../ObservableItemTemplateCollection.cs | 65 ++++++++++++++----- .../src/Core/Properties/AssemblyInfo.cs | 2 + 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs index a2866db1dcfd..f752616e45e9 100644 --- a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs +++ b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs @@ -1,13 +1,13 @@ #nullable disable using System; using System.Collections; -using System.Collections.ObjectModel; +using System.Collections.Generic; using System.Collections.Specialized; -using System.Threading; +using System.Linq; namespace Microsoft.Maui.Controls.Platform { - internal class ObservableItemTemplateCollection : ObservableCollection + internal class ObservableItemTemplateCollection : ObservableList { readonly IList _itemsSource; readonly DataTemplate _itemTemplate; @@ -45,13 +45,10 @@ public ObservableItemTemplateCollection(IList itemsSource, DataTemplate itemTemp if (itemSpacing.HasValue) _itemSpacing = itemSpacing.Value; - for (int n = 0; n < itemsSource.Count; n++) - { - // We're using this as a source for a ListViewBase, and we need INCC to work. So ListViewBase is going - // to iterate over the entire source list right off the bat, no matter what we do. Creating one - // ItemTemplateContext per item in the collection is unavoidable. Luckily, ITC is pretty cheap. - Add(new ItemTemplateContext(itemTemplate, itemsSource[n], container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext)); - } + // We're using this as a source for a ListViewBase, and we need INCC to work. So ListViewBase is going + // to iterate over the entire source list right off the bat, no matter what we do. Creating one + // ItemTemplateContext per item in the collection is unavoidable. Luckily, ITC is pretty cheap. + AddItemTemplateContexts(itemsSource); _collectionChanged = InnerCollectionChanged; _proxy.Subscribe(notifyCollectionChanged, _collectionChanged); @@ -162,11 +159,33 @@ void Add(NotifyCollectionChangedEventArgs args) { var startIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _itemsSource.IndexOf(args.NewItems[0]); - var count = args.NewItems.Count; + AddItemTemplateContexts(args.NewItems, startIndex); + } + + private void AddItemTemplateContexts(IList newItems, int startIndex = 0) + { + var count = newItems.Count; + + if (count == 1) + { + Add(new ItemTemplateContext(_itemTemplate, newItems[0], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext)); + return; + } + + List itemsToAdd = []; for (int n = 0; n < count; n++) { - Insert(startIndex, new ItemTemplateContext(_itemTemplate, args.NewItems[n], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext)); + itemsToAdd.Add(new ItemTemplateContext(_itemTemplate, newItems[n], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext)); + } + + if (startIndex == Items.Count) + { + AddRange(itemsToAdd); + } + else + { + InsertRange(startIndex, itemsToAdd); } } @@ -204,10 +223,21 @@ void Remove(NotifyCollectionChangedEventArgs args) var count = args.OldItems.Count; + if (count == 1) + { + Remove(Items[startIndex]); + return; + } + + List itemsToRemove = []; + for (int n = startIndex + count - 1; n >= startIndex; n--) { - RemoveAt(n); + var itemToRemve = Items[n]; + itemsToRemove.Add(itemToRemve); } + + RemoveRange(itemsToRemove); } void Replace(NotifyCollectionChangedEventArgs args) @@ -216,15 +246,16 @@ void Replace(NotifyCollectionChangedEventArgs args) if (newItemCount == args.OldItems.Count) { + var items = Items.ToList(); + for (int n = 0; n < newItemCount; n++) { var index = args.OldStartingIndex + n; - var oldItem = this[index]; var newItem = new ItemTemplateContext(_itemTemplate, args.NewItems[n], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext); - Items[index] = newItem; - var update = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index); - OnCollectionChanged(update); + items[index] = newItem; } + + ReplaceRange(args.OldStartingIndex, items); } else { diff --git a/src/Controls/src/Core/Properties/AssemblyInfo.cs b/src/Controls/src/Core/Properties/AssemblyInfo.cs index e474d2c631af..b430b01ae921 100644 --- a/src/Controls/src/Core/Properties/AssemblyInfo.cs +++ b/src/Controls/src/Core/Properties/AssemblyInfo.cs @@ -5,6 +5,8 @@ using Compatibility = Microsoft.Maui.Controls.Compatibility; [assembly: InternalsVisibleTo("iOSUnitTests")] +[assembly: InternalsVisibleTo("Microsoft.Maui.Benchmarks.Droid")] +[assembly: InternalsVisibleTo("Microsoft.Maui.Benchmarks.WinUI")] [assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility.ControlGallery")] [assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility")] [assembly: InternalsVisibleTo("Microsoft.Maui.Controls.Compatibility.Android")] From 29fc617b5da9140447495c22fe8fba856c9a4357 Mon Sep 17 00:00:00 2001 From: Edward Miller Date: Fri, 12 Jul 2024 23:19:35 -0500 Subject: [PATCH 2/2] initialize lists with capacity --- .../CollectionView/ObservableItemTemplateCollection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs index f752616e45e9..808c2f679de3 100644 --- a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs +++ b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs @@ -172,7 +172,7 @@ private void AddItemTemplateContexts(IList newItems, int startIndex = 0) return; } - List itemsToAdd = []; + List itemsToAdd = new(capacity: count); for (int n = 0; n < count; n++) { @@ -229,7 +229,7 @@ void Remove(NotifyCollectionChangedEventArgs args) return; } - List itemsToRemove = []; + List itemsToRemove = new(capacity: count); for (int n = startIndex + count - 1; n >= startIndex; n--) {