From 9c646179370b667d588658259e6774e1d0566c9d Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Fri, 22 Mar 2024 13:46:33 -0500 Subject: [PATCH 1/2] Revert "[iOS] Clear BindingContext when cell is queued for reuse (#14619)" This reverts commit 170a7a9832b2179c661a8999a79b9e77b24f9825. # Conflicts: # src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs # src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.cs --- .../Handlers/Items/iOS/ItemsViewDelegator.cs | 17 +---- .../Core/Handlers/Items/iOS/TemplatedCell.cs | 29 ++++---- .../PublicAPI/net-ios/PublicAPI.Unshipped.txt | 1 - .../net-maccatalyst/PublicAPI.Unshipped.txt | 1 - .../CollectionView/CollectionViewTests.cs | 66 +------------------ 5 files changed, 14 insertions(+), 100 deletions(-) diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs index 49d0124ea87e..b5cb4ae4d460 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs @@ -107,21 +107,8 @@ public override nfloat GetMinimumLineSpacingForSection(UICollectionView collecti } public override void CellDisplayingEnded(UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath) - { - if (cell is TemplatedCell templatedCell && - (templatedCell.PlatformHandler?.VirtualView as View)?.BindingContext is object bindingContext) - { - // We want to unbind a cell that is no longer present in the items source. Unfortunately - // it's too expensive to check directly, so let's check that the current binding context - // matches the item at a given position. - - var itemsSource = ViewController?.ItemsSource; - if (itemsSource is null || - !itemsSource.IsIndexPathValid(indexPath) || - !Equals(itemsSource[indexPath], bindingContext)) - { - templatedCell.Unbind(); - } + { + } } diff --git a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs index 404711e1134b..00951238d0aa 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/TemplatedCell.cs @@ -58,15 +58,6 @@ protected void ClearConstraints() ConstrainedDimension = default; } - internal void Unbind() - { - if (PlatformHandler?.VirtualView is View view) - { - view.MeasureInvalidated -= MeasureInvalidated; - view.BindingContext = null; - } - } - public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittingAttributes( UICollectionViewLayoutAttributes layoutAttributes) { @@ -125,12 +116,6 @@ protected void Layout(CGSize constraints) _size = rectangle.Size; } - public override void PrepareForReuse() - { - Unbind(); - base.PrepareForReuse(); - } - public void Bind(DataTemplate template, object bindingContext, ItemsView itemsView) { var oldElement = PlatformHandler?.VirtualView as View; @@ -172,10 +157,18 @@ public void Bind(DataTemplate template, object bindingContext, ItemsView itemsVi // Same template if (oldElement != null) { - oldElement.BindingContext = bindingContext; - oldElement.MeasureInvalidated += MeasureInvalidated; + if (oldElement.BindingContext == null || !(oldElement.BindingContext.Equals(bindingContext))) + { + // If the data is different, update it - UpdateCellSize(); + // Unhook the MeasureInvalidated handler, otherwise it'll fire for every invalidation during the + // BindingContext change + oldElement.MeasureInvalidated -= MeasureInvalidated; + oldElement.BindingContext = bindingContext; + oldElement.MeasureInvalidated += MeasureInvalidated; + + UpdateCellSize(); + } } } diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index 73e385879162..372af46ad8a0 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -108,7 +108,6 @@ Microsoft.Maui.Controls.Region.Equals(Microsoft.Maui.Controls.Region other) -> b Microsoft.Maui.Controls.Shapes.Matrix.Equals(Microsoft.Maui.Controls.Shapes.Matrix other) -> bool Microsoft.Maui.Controls.Shapes.Shape.~Shape() -> void Microsoft.Maui.Controls.VisualElement.~VisualElement() -> void -override Microsoft.Maui.Controls.Handlers.Items.TemplatedCell.PrepareForReuse() -> void override Microsoft.Maui.Controls.Handlers.Compatibility.PhoneFlyoutPageRenderer.ViewWillLayoutSubviews() -> void override Microsoft.Maui.Controls.LayoutOptions.GetHashCode() -> int override Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.TitleViewContainer.LayoutSubviews() -> void diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index c2eaaf95e009..6dd4fea51a73 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -107,7 +107,6 @@ Microsoft.Maui.Controls.Region.Equals(Microsoft.Maui.Controls.Region other) -> b Microsoft.Maui.Controls.Shapes.Matrix.Equals(Microsoft.Maui.Controls.Shapes.Matrix other) -> bool Microsoft.Maui.Controls.Shapes.Shape.~Shape() -> void Microsoft.Maui.Controls.VisualElement.~VisualElement() -> void -override Microsoft.Maui.Controls.Handlers.Items.TemplatedCell.PrepareForReuse() -> void override Microsoft.Maui.Controls.Handlers.Compatibility.PhoneFlyoutPageRenderer.ViewWillLayoutSubviews() -> void override Microsoft.Maui.Controls.LayoutOptions.GetHashCode() -> int override Microsoft.Maui.Controls.Platform.Compatibility.ShellPageRendererTracker.TitleViewContainer.LayoutSubviews() -> void diff --git a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.cs b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.cs index 36ad00303c9d..12168ec75234 100644 --- a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.cs +++ b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.cs @@ -397,71 +397,7 @@ static async Task WaitForUIUpdate(Rect frame, CollectionView collectionView, int timeout -= interval; } } - - [Fact] - public async Task ClearingItemsSourceClearsBindingContext() - { - SetupBuilder(); - - IReadOnlyList logicalChildren = null; - var collectionView = new CollectionView - { - ItemTemplate = new DataTemplate(() => new Label() { HeightRequest = 30, WidthRequest = 200 }), - WidthRequest = 200, - HeightRequest = 200, - }; - - await CreateHandlerAndAddToWindow(collectionView, async handler => - { - var data = new ObservableCollection() - { - new MyRecord("Item 1"), - new MyRecord("Item 2"), - new MyRecord("Item 3"), - }; - collectionView.ItemsSource = data; - await Task.Delay(100); - - logicalChildren = collectionView.LogicalChildrenInternal; - Assert.NotNull(logicalChildren); - Assert.True(logicalChildren.Count == 3); - - // Clear collection - var savedItems = data.ToArray(); - data.Clear(); - - await Task.Delay(100); - - // Check that all logical children have no binding context - foreach (var logicalChild in logicalChildren) - { - Assert.Null(logicalChild.BindingContext); - } - - // Re-add the old children - foreach (var savedItem in savedItems) - { - data.Add(savedItem); - } - - await Task.Delay(100); - - // Check that the right number of logical children have binding context again - int boundChildren = 0; - foreach (var logicalChild in logicalChildren) - { - if (logicalChild.BindingContext is not null) - { - boundChildren++; - } - } - Assert.Equal(3, boundChildren); - }); - } - - record MyRecord(string Name); - - + [Fact] public async Task SettingSelectedItemAfterModifyingCollectionDoesntCrash() { From 105bd80de324a8bfda2a922fe3ba93f7096a1a8f Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Fri, 22 Mar 2024 14:32:20 -0500 Subject: [PATCH 2/2] Update ItemsViewDelegator.cs --- .../src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs index b5cb4ae4d460..cfe29847b6e9 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs @@ -107,9 +107,7 @@ public override nfloat GetMinimumLineSpacingForSection(UICollectionView collecti } public override void CellDisplayingEnded(UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath) - { - - } + { } protected virtual (bool VisibleItems, NSIndexPath First, NSIndexPath Center, NSIndexPath Last) GetVisibleItemsIndexPath() @@ -168,4 +166,4 @@ public override CGSize GetSizeForItem(UICollectionView collectionView, UICollect return ViewController?.GetSizeForItem(indexPath) ?? CGSize.Empty; } } -} \ No newline at end of file +}