Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/Controls/src/Core/IndicatorView/IndicatorView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,18 @@ public partial class IndicatorView : TemplatedView, ITemplatedIndicatorView

static readonly BindableProperty IndicatorLayoutProperty = BindableProperty.Create(nameof(IndicatorLayout), typeof(IBindableLayout), typeof(IndicatorView), null, propertyChanged: TemplateUtilities.OnContentChanged);

readonly WeakNotifyCollectionChangedProxy _collectionChangedProxy = new();
readonly NotifyCollectionChangedEventHandler _collectionChangedEventHandler;

/// <summary>
/// Initializes a new instance of the <see cref="IndicatorView"/> class.
/// </summary>
public IndicatorView() { }
public IndicatorView()
{
_collectionChangedEventHandler = OnCollectionChanged;
}

~IndicatorView() => _collectionChangedProxy.Unsubscribe();

/// <summary>
/// Gets or sets the shape of the indicators.
Expand Down Expand Up @@ -237,11 +245,11 @@ static void UpdateIndicatorLayout(IndicatorView indicatorView, object newValue)

void ResetItemsSource(IEnumerable oldItemsSource)
{
if (oldItemsSource is INotifyCollectionChanged oldCollection)
oldCollection.CollectionChanged -= OnCollectionChanged;
if (oldItemsSource is INotifyCollectionChanged)
_collectionChangedProxy.Unsubscribe();

if (ItemsSource is INotifyCollectionChanged collection)
collection.CollectionChanged += OnCollectionChanged;
_collectionChangedProxy.Subscribe(collection, _collectionChangedEventHandler);

OnCollectionChanged(ItemsSource, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView<TItemsView, TAdapter, TItemsViewSource>.OnAttachedToWindow() -> void
override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView<TItemsView, TAdapter, TItemsViewSource>.OnDetachedFromWindow() -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
~override Microsoft.Maui.Controls.Handlers.Items.MauiRecyclerView<TItemsView, TAdapter, TItemsViewSource>.OnVisibilityChanged(Android.Views.View changedView, Android.Views.ViewStates visibility) -> void
override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.OnHiddenChanged(bool hidden) -> void
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2<TItemsView>.NumberOfSections(UIKit.UICollectionView collectionView) -> nint
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2<TItemsView>.UpdateFlowDirection() -> void

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[moderate] Public API Surface — This adds a duplicate StructuredItemsViewController2<TItemsView>.UpdateFlowDirection() baseline entry; the same signature is already present in this file at line 6. The same accidental duplicate appears in net-maccatalyst/PublicAPI.Unshipped.txt. Please remove these unrelated duplicates and leave only the IndicatorView.~IndicatorView() entries for this fix.

Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
~override Microsoft.Maui.Controls.SwipeItems.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Label.~Label() -> void
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2<TItemsView>.NumberOfSections(UIKit.UICollectionView collectionView) -> nint
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.Handlers.Items2.StructuredItemsViewController2<TItemsView>.UpdateFlowDirection() -> void
Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
~override Microsoft.Maui.Controls.SwipeItems.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Label.~Label() -> void
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
~override Microsoft.Maui.Controls.SwipeItems.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Label.~Label() -> void
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
override Microsoft.Maui.Controls.Handlers.Items.CarouselViewHandler.UpdateEmptyViewVisibility() -> void
~override Microsoft.Maui.Controls.SwipeItems.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Label.~Label() -> void
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
~override Microsoft.Maui.Controls.SwipeItems.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Label.~Label() -> void
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ override Microsoft.Maui.Controls.Shapes.Shape.OnPropertyChanged(string? property
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
override Microsoft.Maui.Controls.GraphicsView.OnBindingContextChanged() -> void
override Microsoft.Maui.Controls.TitleBar.OnBindingContextChanged() -> void
Microsoft.Maui.Controls.IndicatorView.~IndicatorView() -> void
~override Microsoft.Maui.Controls.SwipeItems.OnPropertyChanged(string propertyName = null) -> void
Microsoft.Maui.Controls.Label.~Label() -> void
21 changes: 21 additions & 0 deletions src/Controls/tests/Core.UnitTests/IndicatorViewLayoutTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Xunit;

namespace Microsoft.Maui.Controls.Core.UnitTests
Expand Down Expand Up @@ -65,5 +68,23 @@ public void IndicatorLayout_ShouldBeRemovedWhenIndicatorTemplateIsNulled()
//Assert
Assert.Null(indicatorView.IndicatorLayout);
}

[Fact, Category(TestCategory.Memory)]
public async Task IndicatorViewItemsSourceDoesNotLeak()
{
// A long-lived collection - e.g. a ViewModel's items that is also bound to a
// CarouselView - must not keep every IndicatorView it was assigned to alive.
var sharedItemsSource = new ObservableCollection<string> { "a", "b", "c" };

WeakReference weakIndicatorView;
{
var indicatorView = new IndicatorView();
indicatorView.ItemsSource = sharedItemsSource;
weakIndicatorView = new WeakReference(indicatorView);
}

Assert.False(await weakIndicatorView.WaitForCollect(), "IndicatorView should not be alive!");
GC.KeepAlive(sharedItemsSource);
}
}
}
Loading