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

CollectionView Header/Footer/EmptyView issues when adding/removing items. #24830

Merged
merged 9 commits into from
Oct 10, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ public void NotifyItemInserted(IItemsViewSource source, int startIndex)
if (IsValidAdapter())
{
_adapter.NotifyItemInserted(startIndex);

var changedCount = _adapter.ItemCount - startIndex;
_adapter.NotifyItemRangeChanged(startIndex, changedCount);
}
}

Expand All @@ -40,10 +37,6 @@ public void NotifyItemMoved(IItemsViewSource source, int fromPosition, int toPos
if (IsValidAdapter())
{
_adapter.NotifyItemMoved(fromPosition, toPosition);

var minPosition = System.Math.Min(fromPosition, toPosition);
var changedCount = _adapter.ItemCount - minPosition;
_adapter.NotifyItemRangeChanged(minPosition, changedCount);
}
}

Expand All @@ -58,9 +51,6 @@ public void NotifyItemRangeInserted(IItemsViewSource source, int startIndex, int
if (IsValidAdapter())
{
_adapter.NotifyItemRangeInserted(startIndex, count);

var changedCount = _adapter.ItemCount - startIndex;
_adapter.NotifyItemRangeChanged(startIndex, changedCount);
}
}

Expand All @@ -69,9 +59,6 @@ public void NotifyItemRangeRemoved(IItemsViewSource source, int startIndex, int
if (IsValidAdapter())
{
_adapter.NotifyItemRangeRemoved(startIndex, count);

var changedCount = _adapter.ItemCount - startIndex;
_adapter.NotifyItemRangeChanged(startIndex, changedCount);
}
}

Expand All @@ -80,9 +67,6 @@ public void NotifyItemRemoved(IItemsViewSource source, int startIndex)
if (IsValidAdapter())
{
_adapter.NotifyItemRemoved(startIndex);

var changedCount = _adapter.ItemCount - startIndex;
_adapter.NotifyItemRangeChanged(startIndex, changedCount);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,11 +567,12 @@ internal void UpdateEmptyViewVisibility()
itemCount++;
}

var showEmptyView = ItemsView?.EmptyView != null && ItemsViewAdapter.ItemCount == itemCount;
var showEmptyView = (ItemsView?.EmptyView is not null || ItemsView?.EmptyViewTemplate is not null) && ItemsViewAdapter.ItemCount == itemCount;

var currentAdapter = GetAdapter();
if (showEmptyView && currentAdapter != _emptyViewAdapter)
{
GetRecycledViewPool().Clear();
SwapAdapter(_emptyViewAdapter, true);

// TODO hartez 2018/10/24 17:34:36 If this works, cache this layout manager as _emptyLayoutManager
Expand All @@ -580,6 +581,7 @@ internal void UpdateEmptyViewVisibility()
}
else if (!showEmptyView && currentAdapter != ItemsViewAdapter)
{
GetRecycledViewPool().Clear();
SwapAdapter(ItemsViewAdapter, true);
UpdateLayoutManager();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ protected virtual void HandleFormsElementMeasureInvalidated(VisualElement formsE
internal void UpdateView(object view, DataTemplate viewTemplate, ref UIView uiView, ref VisualElement formsElement)
{
// Is view set on the ItemsView?
if (view == null)
if (view is null && viewTemplate is null)
{
if (formsElement != null)
{
Expand Down
57 changes: 57 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue11896.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?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.Issue11896"
xmlns:local="clr-namespace:Maui.Controls.Sample.Issues"
x:Name="ThisMainPage"
Title="Main Page">

<Grid RowDefinitions="Auto,*">

<HorizontalStackLayout
Grid.Row="0"
Padding="20"
HorizontalOptions="Center"
Spacing="20">
<Button Command="{Binding AddCommand}" Text="Add item" AutomationId="AddButton" />
<Button Command="{Binding RemoveCommand}" Text="Remove item" AutomationId="RemoveButton" />
</HorizontalStackLayout>

<CollectionView Grid.Row="1" ItemsSource="{Binding ItemList}">

<CollectionView.HeaderTemplate>
<DataTemplate>
<Label
Padding="10"
FontAttributes="Bold"
FontSize="Large"
Text="Cities" />
</DataTemplate>
</CollectionView.HeaderTemplate>

<CollectionView.ItemTemplate>
<DataTemplate>
<Label Padding="20,5,5,5" Text="{Binding .}" />
</DataTemplate>
</CollectionView.ItemTemplate>

<CollectionView.EmptyViewTemplate>
<DataTemplate>
<Label Padding="20,5,5,5" Text="Empty" />
</DataTemplate>
</CollectionView.EmptyViewTemplate>

<CollectionView.FooterTemplate>
<DataTemplate>
<Label
Padding="10"
FontAttributes="Bold"
FontSize="Large"
Text="Hello world !!!" />
</DataTemplate>
</CollectionView.FooterTemplate>

</CollectionView>

</Grid>
</ContentPage>
74 changes: 74 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue11896.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using System.Collections.Specialized;


namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 11896, "CollectionView Header/Footer/EmptyView issues when adding/removing items", PlatformAffected.Android)]
public partial class Issue11896
{
public Issue11896()
{
InitializeComponent();
BindingContext = new Issue11896ViewModel();
}
}
internal class Issue11896ViewModel
{
// Define a static list of cities to add to the collection
private List<string> _staticCities = new()
{
"Paris",
"New York",
"Tokyo",
"Berlin",
"Madrid",
"London"
};

private int _currentIndex = 0;

public ObservableCollection<string> ItemList { get; set; }

public ICommand AddCommand => new Command(Add);
public ICommand RemoveCommand => new Command(Remove);

public Issue11896ViewModel()
{
// Initialize the ItemList
ItemList = new ObservableCollection<string>();
}

private void Add()
{
// Add the next city from the static list
if (_currentIndex < _staticCities.Count)
{
ItemList.Add(_staticCities[_currentIndex]);
_currentIndex++;
}
else
{
// Optionally reset the index or handle the end of the list as needed
_currentIndex = 0; // Resetting to allow cycling through the list again
}
}

private void Remove()
{
// Remove the last item in the list if any exist
if (ItemList.Count > 0)
{
ItemList.RemoveAt(ItemList.Count - 1);
// Decrement the index to ensure the correct city is added next time
_currentIndex = Math.Max(0, _currentIndex - 1);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#if !MACCATALYST && !WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

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

public override string Issue => "CollectionView Header/Footer/EmptyView issues when adding/removing items";

[Test]

[Category(UITestCategories.CollectionView)]
public void CollectionviewFooterHideswhenDynamicallyAddorRemoveItems()
{
App.WaitForElement("AddButton");
App.Tap("AddButton");
App.Tap("AddButton");
App.Tap("AddButton");
// Here we check for Footer proper visibility with proper alignment in view.
VerifyScreenshot();
}

[Test]
[Category(UITestCategories.CollectionView)]
public void CollectionViewHeaderBlankWhenLastItemRemoved()
{
App.WaitForElement("AddButton");
App.Tap("AddButton");
App.Tap("AddButton");
App.Tap("RemoveButton");
App.Tap("RemoveButton");
App.Tap("AddButton");
// Here we check for Header and Footer proper visibility with proper alignment in view.
VerifyScreenshot();
}
}
}
#endif
Loading