diff --git a/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs b/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs index 1889e038319e..07c0eefceeb2 100644 --- a/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs +++ b/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs @@ -229,7 +229,7 @@ internal int ItemsCount() internal object ElementAt(int index) { if (_itemsSource is IList list) - return list[index]; + return (index >= 0 && index < list.Count) ? list[index] : null; int count = 0; foreach (var item in _itemsSource) @@ -239,7 +239,7 @@ internal object ElementAt(int index) count++; } - return -1; + return null; } } } diff --git a/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs b/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs index ab43bb156c0a..e0b11f6650ad 100644 --- a/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs +++ b/src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs @@ -374,15 +374,8 @@ protected virtual void UpdateItemsUpdatingScrollMode() if (ItemsViewAdapter == null || ItemsView == null) return; - if (ItemsView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepItemsInView) - { - // Keeping the current items in view is the default, so we don't need to watch for data changes - _itemsUpdateScrollObserver.Stop(ItemsViewAdapter); - } - else - { - _itemsUpdateScrollObserver.Start(ItemsViewAdapter); - } + // We need to observe the adapter for changes for the ItemsUpdatingScrollMode. + _itemsUpdateScrollObserver.Start(ItemsViewAdapter); } public virtual void ScrollTo(ScrollToRequestEventArgs args) @@ -392,6 +385,11 @@ public virtual void ScrollTo(ScrollToRequestEventArgs args) var position = DetermineTargetPosition(args); + if (position < 0) + { + return; + } + if (args.IsAnimated) { ScrollHelper.AnimateScrollToPosition(position, args.ScrollToPosition); @@ -598,6 +596,13 @@ internal void AdjustScrollForItemUpdate() { ScrollHelper.UndoNextScrollAdjustment(); } + else if (ItemsView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepItemsInView) + { + if (GetLayoutManager().ItemCount > 0) + { + ScrollTo(new ScrollToRequestEventArgs(0, 0, Microsoft.Maui.Controls.ScrollToPosition.Start, true)); + } + } } GridLayoutManager CreateGridLayout(GridItemsLayout gridItemsLayout) diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs index c6f8473b629d..9ee1a47ef2f1 100644 --- a/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs +++ b/src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs @@ -5,6 +5,7 @@ using CoreGraphics; using Foundation; using Microsoft.Extensions.Logging; +using Microsoft.Maui.Controls.Handlers.Items2; using UIKit; namespace Microsoft.Maui.Controls.Handlers.Items @@ -500,6 +501,10 @@ public override void FinalizeCollectionViewUpdates() { ForceScrollToLastItem(CollectionView, _itemsLayout); } + else if (ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepItemsInView) + { + ForceScrollToFirstItem(CollectionView, _itemsLayout); + } } void TrackOffsetAdjustment() @@ -565,6 +570,33 @@ static bool UpdateWillShiftVisibleItems(UICollectionView collectionView, UIColle return false; } + static void ForceScrollToFirstItem(UICollectionView collectionView, ItemsLayout itemsLayout) + { + //CarouselView and CarouselView2 handle scrolling based on ItemsUpdatingScrollMode in CarouselViewController. Even when passing the 0th index, issues may arise in CarouselView since the position is determined by the center itemindex. + // Issue Link : https://github.com/dotnet/maui/issues/25991 + if (collectionView.DataSource is CarouselViewController || collectionView.DataSource is CarouselViewController2) + { + return; + } + + //Fix Added for ItemsUpdatingScrollMode.KeepItemsInView + int sections = (int)collectionView.NumberOfSections(); + + if (sections == 0) + { + return; + } + + if (collectionView.NumberOfItemsInSection(0) > 0) + { + var indexPath = NSIndexPath.FromItemSection(0, 0); + if (itemsLayout.Orientation == ItemsLayoutOrientation.Vertical) + collectionView.ScrollToItem(indexPath, UICollectionViewScrollPosition.Top, true); + else + collectionView.ScrollToItem(indexPath, UICollectionViewScrollPosition.Right, true); + } + } + static void ForceScrollToLastItem(UICollectionView collectionView, ItemsLayout itemsLayout) { var sections = (int)collectionView.NumberOfSections(); diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue26810.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue26810.xaml new file mode 100644 index 000000000000..4534f3e97ad9 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue26810.xaml @@ -0,0 +1,65 @@ + + + + +