Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Try scrolling down and when new items are added the first item does not kept in the displayed list. #27153

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -239,7 +239,7 @@ internal object ElementAt(int index)
count++;
}

return -1;
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

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

These CollectionView tests are failing:
image
The App is crashing. Could you check if are related with the changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These CollectionView tests are failing: image The App is crashing. Could you check if are related with the changes?

Hi @jsuarezruiz Yes, the crash is due to the DataObserver included for KeepItemsInView. I have modified ElementAt() and DetermineTargetPosition. Please review the changes and let us know if you have any concerns.

}

public virtual void ScrollTo(ScrollToRequestEventArgs args)
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
32 changes: 32 additions & 0 deletions src/Controls/src/Core/Handlers/Items/iOS/ItemsViewLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -500,6 +501,10 @@ public override void FinalizeCollectionViewUpdates()
{
ForceScrollToLastItem(CollectionView, _itemsLayout);
}
else if (ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepItemsInView)
{
ForceScrollToFirstItem(CollectionView, _itemsLayout);
}
}

void TrackOffsetAdjustment()
Expand Down Expand Up @@ -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();
Expand Down
65 changes: 65 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue26810.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv1="clr-namespace:Maui.Controls.Sample"
x:Class="Maui.Controls.Sample.Issues.Issue26810">
<Grid RowDefinitions="50,50,50,*"
AutomationId="26810MainGrid">
<Grid Grid.Row="0"
ColumnDefinitions="*,*,*">
<Button Text="KeepItemsInView"
AutomationId="26810FirstItemButton"
x:Name="KeepItemsInView"
Grid.Column="0"
Clicked="ItemsUpdatingScrollMode_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Button Text="KeepScrollOffset"
AutomationId="26810ScrollOffsetButton"
x:Name="KeepScrollOffset"
Grid.Column="1"
Clicked="ItemsUpdatingScrollMode_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Button Text="KeepLastItemInView"
AutomationId="26810LastItemButton"
x:Name="KeepLastItemInView"
Grid.Column="2"
Clicked="ItemsUpdatingScrollMode_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
</Grid>
<Button Text="Scroll to Random Item"
AutomationId="26810Button"
x:Name="ScrollToButton"
Grid.Row="1"
Clicked="ScrollToButton_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Button Text="Add New Item"
AutomationId="26810AddButton"
x:Name="AddButton"
Grid.Row="2"
Clicked="AddButton_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<cv1:CollectionView1 x:Name="CollectionView"
AutomationId="26810CollectionView"
Grid.Row="3">
<cv1:CollectionView1.ItemsLayout>
<LinearItemsLayout ItemSpacing="5"
Orientation="Horizontal"/>
</cv1:CollectionView1.ItemsLayout>
<cv1:CollectionView1.ItemTemplate>
<DataTemplate>
<Grid HeightRequest="50">
<Label Text="{Binding Name}"
AutomationId="{Binding AutomationId}"
VerticalOptions="Center"
HorizontalOptions="Center"/>
</Grid>
</DataTemplate>
</cv1:CollectionView1.ItemTemplate>
</cv1:CollectionView1>
</Grid>
</ContentPage>
51 changes: 51 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue26810.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Collections.ObjectModel;

namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 26810, "Scroll To first item in CollectionView when updating the collection with KeepItemsInView",
PlatformAffected.Android)]
public partial class Issue26810 : ContentPage
{
private ObservableCollection<Issue26810ItemModel> Items { get; set; } = new ObservableCollection<Issue26810ItemModel>();
public Issue26810()
{
InitializeComponent();
for (int i = 1; i <= 30; i++)
{
Items.Add(new Issue26810ItemModel { Name = $"Preloaded Item {i}", AutomationId = $"Item{i}" });
}
CollectionView.ItemsSource = Items;
this.BindingContext = this;
}

private void ItemsUpdatingScrollMode_Clicked(object sender, EventArgs e)
{
var button = (Button)sender;
if (button.Text == "KeepScrollOffset")
{
CollectionView.ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepScrollOffset;
}
else if (button.Text == "KeepLastItemInView")
{
CollectionView.ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepLastItemInView;
}
}

private void AddButton_Clicked(object sender, EventArgs e)
{
Items.Add(new Issue26810ItemModel { Name = $"Item {Items.Count + 1}", AutomationId = $"Item{Items.Count + 1}" });
}
private void ScrollToButton_Clicked(object sender, EventArgs e)
{
if (Items.Count > 0)
{
// Scroll to random item
CollectionView.ScrollTo(19, position: ScrollToPosition.End, animate: true);
}
}
}

public class Issue26810ItemModel
{
public string Name { get; set; }
public string AutomationId { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv1="clr-namespace:Maui.Controls.Sample"
x:Class="Maui.Controls.Sample.Issues.Issue26810Vertical">
<Grid RowDefinitions="50,50,50,*"
AutomationId="26810MainGrid">
<Grid Grid.Row="0"
ColumnDefinitions="*,*,*">
<Button Text="KeepItemsInView"
AutomationId="26810FirstItemButton"
x:Name="KeepItemsInView"
Grid.Column="0"
Clicked="ItemsUpdatingScrollMode_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Button Text="KeepScrollOffset"
AutomationId="26810ScrollOffsetButton"
x:Name="KeepScrollOffset"
Grid.Column="1"
Clicked="ItemsUpdatingScrollMode_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Button Text="KeepLastItemInView"
AutomationId="26810LastItemButton"
x:Name="KeepLastItemInView"
Grid.Column="2"
Clicked="ItemsUpdatingScrollMode_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
</Grid>
<Button Text="Scroll to Random Item"
AutomationId="26810Button"
x:Name="ScrollToButton"
Grid.Row="1"
Clicked="ScrollToButton_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<Button Text="Add New Item"
AutomationId="26810AddButton"
x:Name="AddButton"
Grid.Row="2"
Clicked="AddButton_Clicked"
HorizontalOptions="Center"
VerticalOptions="Center"/>
<cv1:CollectionView1 x:Name="CollectionView"
AutomationId="26810CollectionView"
Grid.Row="3">
<cv1:CollectionView1.ItemTemplate>
<DataTemplate>
<Grid HeightRequest="50">
<Label Text="{Binding Name}"
AutomationId="{Binding AutomationId}"
VerticalOptions="Center"
HorizontalOptions="Center"/>
</Grid>
</DataTemplate>
</cv1:CollectionView1.ItemTemplate>
</cv1:CollectionView1>
</Grid>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.ObjectModel;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.None, 26810, "Scroll To first item in CollectionView with vertical orientation when updating the collection with KeepItemsInView",
PlatformAffected.Android)]

public partial class Issue26810Vertical : ContentPage
{

private ObservableCollection<Issue26810ItemModel> Items { get; set; } = new ObservableCollection<Issue26810ItemModel>();
public Issue26810Vertical()
{
InitializeComponent();
for (int i = 1; i <= 30; i++)
{
Items.Add(new Issue26810ItemModel { Name = $"Preloaded Item {i}", AutomationId = $"Item{i}" });
}
CollectionView.ItemsSource = Items;
this.BindingContext = this;
}
private void ItemsUpdatingScrollMode_Clicked(object sender, EventArgs e)
{
var button = (Button)sender;
if (button.Text == "KeepScrollOffset")
{
CollectionView.ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepScrollOffset;
}
else if (button.Text == "KeepLastItemInView")
{
CollectionView.ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepLastItemInView;
}
}

private void AddButton_Clicked(object sender, EventArgs e)
{
Items.Add(new Issue26810ItemModel { Name = $"Item {Items.Count + 1}", AutomationId = $"Item{Items.Count + 1}" });
}
private void ScrollToButton_Clicked(object sender, EventArgs e)
{
if (Items.Count > 0)
{
// Scroll to random item
CollectionView.ScrollTo(19, position: ScrollToPosition.End, animate: true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public CollectionViewItemsUpdatingScrollModeUITests(TestDevice device)

protected override bool ResetAfterEachTest => true;

#if TEST_FAILS_ON_WINDOWS // For more information, see :https://github.com/dotnet/maui/issues/28006
// KeepScrollOffset (src\Compatibility\ControlGallery\src\Issues.Shared\CollectionViewItemsUpdatingScrollMode.cs)
[Test]
[Category(UITestCategories.CollectionView)]
Expand All @@ -30,9 +29,9 @@ public void KeepItemsInView()
App.Click("AddItemAbove");
}

App.WaitForElement("Vegetables.jpg, 10");
App.WaitForNoElement("Vegetables.jpg, 10");
}
#endif


#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // The test fails on iOS and macOS because Appium is unable to locate the Picker control elements resulting in a TimeoutException. For more information, see: https://github.com/dotnet/maui/issues/28024
// KeepScrollOffset (src\Compatibility\ControlGallery\src\Issues.Shared\CollectionViewItemsUpdatingScrollMode.cs)
Expand Down
Loading