diff --git a/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs b/src/Controls/src/Core/Platform/Windows/CollectionView/ObservableItemTemplateCollection.cs index 4e93ed878940..c91f57926f9d 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,12 +159,33 @@ void Add(NotifyCollectionChangedEventArgs args) { var index = 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 = new(capacity: count); for (int n = 0; n < count; n++) { - Insert(index, new ItemTemplateContext(_itemTemplate, args.NewItems[n], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext)); - index++; + itemsToAdd.Add(new ItemTemplateContext(_itemTemplate, newItems[n], _container, _itemHeight, _itemWidth, _itemSpacing, _mauiContext)); + } + + if (startIndex == Items.Count) + { + AddRange(itemsToAdd); + } + else + { + InsertRange(startIndex, itemsToAdd); } } @@ -205,10 +223,21 @@ void Remove(NotifyCollectionChangedEventArgs args) var count = args.OldItems.Count; + if (count == 1) + { + Remove(Items[startIndex]); + return; + } + + List itemsToRemove = new(capacity: count); + for (int n = startIndex + count - 1; n >= startIndex; n--) { - RemoveAt(n); + var itemToRemve = Items[n]; + itemsToRemove.Add(itemToRemve); } + + RemoveRange(itemsToRemove); } void Replace(NotifyCollectionChangedEventArgs args) @@ -217,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 6b85c51555f6..1d4641b4e52f 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")]