diff --git a/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cs b/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cs index 6b08e60c658b..ade34df63ec5 100644 --- a/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cs +++ b/src/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cs @@ -30,6 +30,7 @@ public partial class CarouselViewHandler : ItemsViewHandler bool _isCarouselViewReady; bool _isInternalPositionUpdate; int _gotoPosition = -1; + bool _isCollectionChanged; NotifyCollectionChangedEventHandler _collectionChanged; readonly WeakNotifyCollectionChangedProxy _proxy = new(); @@ -534,6 +535,11 @@ void CarouselScrolled(object sender, ItemsViewScrolledEventArgs e) } var position = e.CenterItemIndex; + if (_isCollectionChanged && ItemsView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepScrollOffset) + { + position = ItemsView.Position; + _isCollectionChanged = false; + } if (position == -1) { @@ -573,7 +579,7 @@ void OnCollectionItemsSourceChanged(object sender, NotifyCollectionChangedEventA { // Set flag to disable animation during collection changes _isInternalPositionUpdate = true; - + try { var carouselPosition = ItemsView.Position; @@ -599,6 +605,12 @@ void OnCollectionItemsSourceChanged(object sender, NotifyCollectionChangedEventA && currentItemPosition != -1) { carouselPosition = currentItemPosition; + _isCollectionChanged = true; + } + + if (e.Action == NotifyCollectionChangedAction.Remove) + { + _isCollectionChanged = true; } if (ItemsView.ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepLastItemInView) diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCarouselViewKeepScrollOffsetAdd.png new file mode 100644 index 000000000000..4c909a70eb22 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/VerifyCarouselViewKeepScrollOffsetAdd.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29421.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29421.cs new file mode 100644 index 000000000000..2048894de54c --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29421.cs @@ -0,0 +1,73 @@ +using System.Collections.ObjectModel; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 29421, "KeepScrollOffset Not Working as Expected in CarouselView", PlatformAffected.UWP)] +public class Issue29421 : ContentPage +{ + public Issue29421() + { + var verticalStackLayout = new VerticalStackLayout(); + var carouselItems = new ObservableCollection + { + "Item 0", + "Item 1", + "Item 2", + "Item 3", + "Item 4", + }; + + CarouselView carouselView = new CarouselView + { + ItemsSource = carouselItems, + AutomationId = "carouselview", + ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepScrollOffset, + HeightRequest = 300, + ItemTemplate = new DataTemplate(() => + { + var grid = new Grid + { + Padding = 10 + }; + + var label = new Label + { + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center, + FontSize = 18, + }; + label.SetBinding(Label.TextProperty, "."); + label.SetBinding(Label.AutomationIdProperty, "."); + + grid.Children.Add(label); + return grid; + }), + HorizontalOptions = LayoutOptions.Fill, + }; + + var indicatorView = new IndicatorView + { + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + carouselView.IndicatorView = indicatorView; + + var addButton = new Button + { + Text = "Add item", + AutomationId = "AddButton", + Margin = new Thickness(20), + }; + + addButton.Clicked += (sender, e) => + { + carouselItems.Insert(0, "NewItem"); + }; + + verticalStackLayout.Children.Add(addButton); + verticalStackLayout.Children.Add(carouselView); + verticalStackLayout.Children.Add(indicatorView); + + Content = verticalStackLayout; + } +} diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselViewKeepScrollOffsetAdd.png new file mode 100644 index 000000000000..4e8bf912289d Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselViewKeepScrollOffsetAdd.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29421.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29421.cs new file mode 100644 index 000000000000..2207615a1a16 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29421.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue29421 : _IssuesUITest +{ + public override string Issue => "KeepScrollOffset Not Working as Expected in CarouselView"; + + public Issue29421(TestDevice device) + : base(device) + { } + + [Test] + [Category(UITestCategories.CarouselView)] + public void VerifyCarouselViewKeepScrollOffsetAdd() + { + App.WaitForElement("carouselview"); + App.Tap("AddButton"); + VerifyScreenshot(); + } +} diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyCarouselViewKeepScrollOffsetAdd.png new file mode 100644 index 000000000000..1f750dcf13cb Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyCarouselViewKeepScrollOffsetAdd.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCarouselViewKeepScrollOffsetAdd.png new file mode 100644 index 000000000000..07df09c0518d Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCarouselViewKeepScrollOffsetAdd.png differ diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselViewKeepScrollOffsetAdd.png new file mode 100644 index 000000000000..b0c5ca480398 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselViewKeepScrollOffsetAdd.png differ