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 CollectionView with RefreshView when ObservableCollection<T>.Clear() throws System.ArgumentOutOfRangeException #26573

Merged
merged 3 commits into from
Jan 6, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,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 @@ -289,7 +289,7 @@ internal object ElementAt(int index)
count++;
}

return -1;
return null;
}

internal int IndexOf(object item)
Expand Down
44 changes: 44 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue23868.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?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"
x:Class="Maui.Controls.Sample.Issues.Issue23868"
xmlns:local="clr-namespace:Maui.Controls.Sample.Issues"
x:Name="ThisMainPage"
Title="Main Page">
<StackLayout>
<Label Text="Pull to refresh twice to replicate the issue"
HorizontalOptions="Center"
VerticalOptions="Center"
FontSize="16"
Margin="10"
TextColor="Red" />
<RefreshView x:Name="refreshView" Command="{Binding PullToRefreshCommand}" Refreshing="OnRefreshing">
<CollectionView AutomationId="CollectionView" x:Name="collectionView" ItemsSource="{Binding Items}">
<CollectionView.Header>
<Grid ColumnDefinitions="2*,*"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding Source={x:Reference collectionView}, Path=ItemsSource.Count, FallbackValue=False}">
<Label Grid.Column="0"
Text="Items Count:"
VerticalTextAlignment="Center"
HorizontalOptions="EndAndExpand"/>
<Label Grid.Column="1"
Text="{Binding Items.Count}"
VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand"/>
</Grid>
</CollectionView.Header>
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding}" HorizontalOptions="Center" VerticalOptions="Center"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>

<StackLayout Orientation="Horizontal" HorizontalOptions="Center" Margin="10">
<Button Text="UpdateData" AutomationId="UpdateData" Clicked="OnUpdateDataClicked"/>
<Button Text="ClearData" Clicked="OnClearDataClicked"/>
</StackLayout>
</StackLayout>
</ContentPage>
75 changes: 75 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue23868.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#nullable enable
using System.Collections.ObjectModel;
using System.Text.Json;

namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 23868, "CollectionView with RefreshView Throws Exception During Pull-to-Refresh Actions", PlatformAffected.iOS)]
public partial class Issue23868: ContentPage
{
private ObservableCollection<string> _items = new ObservableCollection<string>();

public ObservableCollection<string> Items
{
get => _items;
set
{
_items = value;
collectionView.ItemsSource = _items;
}
}

public Command PullToRefreshCommand { get; }

public Issue23868()
{
InitializeComponent();
Items = new ObservableCollection<string>();
BindingContext = this;

PullToRefreshCommand = new Command(async () => await SimulateHttpRequest());
}

private async Task SimulateHttpRequest()
{
refreshView.IsRefreshing = true;

// Simulate delay for data fetching
await Task.Delay(200);

// Simulated local JSON data
string jsonData = "[\"Local Item 1\", \"Local Item 2\", \"Local Item 3\"]";

// Parse the local data as a response
var items = JsonSerializer.Deserialize<string[]>(jsonData);

// Update ObservableCollection with fetched data
if (items != null)
{
Items.Clear();
foreach (var item in items)
{
Items.Add(item);
}
}

refreshView.IsRefreshing = false;
}

private async void OnUpdateDataClicked(object sender, EventArgs e)
{
await SimulateHttpRequest();
}

private void OnClearDataClicked(object sender, EventArgs e)
{
Items.Clear();
}

private void OnRefreshing(object sender, EventArgs e)
{
PullToRefreshCommand.Execute(null);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#if TEST_FAILS_ON_CATALYST //Swipe actions cannot be performed on the macOS test server
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues
{
internal class Issue23868 : _IssuesUITest
{
public Issue23868(TestDevice device) : base(device) { }

public override string Issue => "CollectionView with RefreshView Throws Exception During Pull-to-Refresh Actions";

[Test]
[Category(UITestCategories.CollectionView)]
public void CollectionViewWithHeaderAndRefreshViewShouldNotCrashOnPullToRefresh()
{
App.WaitForElement("UpdateData");
App.Tap("UpdateData");
App.WaitForElement("CollectionView");
App.ScrollUp("CollectionView", ScrollStrategy.Gesture, swipeSpeed: 7000);
App.WaitForElement("CollectionView");
App.ScrollUp("CollectionView", ScrollStrategy.Gesture, swipeSpeed: 7000);
App.WaitForElement("CollectionView");
}
}
}
#endif
Loading