diff --git a/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs b/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs
index f2b7565c4ef4..596f1224c28a 100644
--- a/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs
+++ b/src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs
@@ -226,10 +226,13 @@ void UpdateGroupTracking()
for (int n = 0; n < _groupSource.Count; n++)
{
- var source = ItemsSourceFactory.Create(_groupSource[n] as IEnumerable, _groupableItemsView, this);
- source.HasFooter = _hasGroupFooters;
- source.HasHeader = _hasGroupHeaders;
- _groups.Add(source);
+ if (_groupSource[n] is IEnumerable list)
+ {
+ var source = ItemsSourceFactory.Create(list, _groupableItemsView, this);
+ source.HasFooter = _hasGroupFooters;
+ source.HasHeader = _hasGroupHeaders;
+ _groups.Add(source);
+ }
}
}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml
new file mode 100644
index 000000000000..0f8a263f8510
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml.cs
new file mode 100644
index 000000000000..197efee92a37
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml.cs
@@ -0,0 +1,216 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 28827, "[Android] Group Header/Footer set for all Items when IsGrouped is True for ObservableCollection", PlatformAffected.Android)]
+public partial class Issue28827 : ContentPage
+{
+ Issue28827CollectionViewViewModel _viewModel;
+ public Issue28827()
+ {
+ InitializeComponent();
+ BindingContext = _viewModel = new Issue28827CollectionViewViewModel();
+ }
+
+ void OnGroupHeaderTemplateChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (GroupHeaderTemplateNone.IsChecked)
+ {
+ _viewModel.GroupHeaderTemplate = null;
+ }
+ else if (GroupHeaderTemplateGrid.IsChecked)
+ {
+ _viewModel.GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ return new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10),
+ Children =
+ {
+ new Label
+ {
+ Text = "Group Header Template (Grid View)",
+ FontSize = 18,
+ AutomationId = "GroupHeaderTemplate",
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Green
+ }
+ }
+ };
+ });
+ }
+ }
+
+ void OnGroupFooterTemplateChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (GroupFooterTemplateNone.IsChecked)
+ {
+ _viewModel.GroupFooterTemplate = null;
+ }
+ else if (GroupFooterTemplateGrid.IsChecked)
+ {
+ _viewModel.GroupFooterTemplate = new DataTemplate(() =>
+ {
+ return new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = new Thickness(10),
+ Children =
+ {
+ new Label
+ {
+ Text = "Group Footer Template (Grid View)",
+ FontSize = 18,
+ AutomationId = "GroupFooterTemplate",
+ FontAttributes = FontAttributes.Bold,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Red
+ }
+ }
+ };
+ });
+ }
+ }
+
+ void OnIsGroupedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ if (IsGroupedFalse.IsChecked)
+ {
+ _viewModel.IsGrouped = false;
+ }
+ else if (IsGroupedTrue.IsChecked)
+ {
+ _viewModel.IsGrouped = true;
+ }
+ }
+}
+
+public class Issue28827CollectionViewViewModel : INotifyPropertyChanged
+{
+ DataTemplate _groupHeaderTemplate;
+ DataTemplate _groupFooterTemplate;
+ DataTemplate _itemTemplate;
+ bool _isGrouped = false;
+ ObservableCollection _observableCollection;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public Issue28827CollectionViewViewModel()
+ {
+ LoadItems();
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var stackLayout = new StackLayout
+ {
+ Padding = new Thickness(10),
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var label = new Label
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ label.SetBinding(Label.TextProperty, "Caption");
+ stackLayout.Children.Add(label);
+ return stackLayout;
+ });
+
+ GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ var stackLayout = new StackLayout
+ {
+ BackgroundColor = Colors.LightGray
+ };
+ var label = new Label
+ {
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 18
+ };
+ label.SetBinding(Label.TextProperty, "Key");
+ stackLayout.Children.Add(label);
+ return stackLayout;
+ });
+ }
+
+ void LoadItems()
+ {
+ _observableCollection = new ObservableCollection
+ {
+ new Issue28827ItemModel { Caption = "Item 1" },
+ new Issue28827ItemModel { Caption = "Item 2" },
+ new Issue28827ItemModel { Caption = "Item 3" }
+ };
+ }
+
+ public DataTemplate GroupHeaderTemplate
+ {
+ get => _groupHeaderTemplate;
+ set { _groupHeaderTemplate = value; OnPropertyChanged(); }
+ }
+
+ public DataTemplate GroupFooterTemplate
+ {
+ get => _groupFooterTemplate;
+ set { _groupFooterTemplate = value; OnPropertyChanged(); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get => _itemTemplate;
+ set { _itemTemplate = value; OnPropertyChanged(); }
+ }
+
+ public bool IsGrouped
+ {
+ get => _isGrouped;
+ set { _isGrouped = value; OnPropertyChanged(); }
+ }
+
+ public object ItemsSource
+ {
+ get => _observableCollection;
+ }
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ if (propertyName == nameof(IsGrouped))
+ {
+ OnPropertyChanged(nameof(ItemsSource));
+ }
+
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+}
+
+internal class Issue28827Grouping : List
+{
+ public TKey Key { get; }
+
+ public Issue28827Grouping(TKey key, List items) : base(items)
+ {
+ Key = key;
+ }
+
+ public override string ToString()
+ {
+ return Key?.ToString() ?? base.ToString();
+ }
+}
+
+internal class Issue28827ItemModel
+{
+ public string Caption { get; set; }
+
+ public override string ToString()
+ {
+ return !string.IsNullOrEmpty(Caption) ? Caption : base.ToString();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28827.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28827.cs
new file mode 100644
index 000000000000..aa461914f578
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28827.cs
@@ -0,0 +1,28 @@
+#if TEST_FAILS_ON_WINDOWS // NullReferenceException occurs when switching isGrouped to true
+// refer to https://github.com/dotnet/maui/issues/28824
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue28827 : _IssuesUITest
+{
+ public override string Issue => "[Android] Group Header/Footer set for all Items when IsGrouped is True for ObservableCollection";
+ public Issue28827(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CVGroupHFTemplateWithObservableCollection()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("IsGroupedTrue");
+ App.Tap("GroupHeaderTemplateGrid");
+ App.Tap("GroupFooterTemplateGrid");
+ App.WaitForNoElement("GroupHeaderTemplate");
+ App.WaitForNoElement("GroupFooterTemplate");
+ }
+}
+#endif
\ No newline at end of file