[Android] Fixed issue where group Header/Footer template was applied to all items when IsGrouped was true for an ObservableCollection#28886
Conversation
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28827.cs
Outdated
Show resolved
Hide resolved
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
ce36a2d to
41a91ce
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 28886Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 28886" |
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where CollectionView group headers and footers were incorrectly applied to all items when IsGrouped was set to true with a non-grouped ObservableCollection<T>. The root cause was that individual items were being cast to IEnumerable unconditionally, causing them to be treated as groups even when they weren't enumerable collections.
Key changes:
- Added type check to ensure only items implementing
IEnumerableare treated as groups in the Android handler - Added comprehensive UI test with proper XAML page and NUnit test implementation
- Fix aligns Android behavior with iOS, which already had this check in place
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableGroupedSource.cs |
Added IEnumerable type check before adding items to groups collection to prevent non-enumerable items from being treated as groups |
src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml |
Created test page UI with CollectionView and radio button controls to toggle grouping and templates |
src/Controls/tests/TestCases.HostApp/Issues/Issue28827.xaml.cs |
Implemented view model and event handlers for testing grouped collection behavior with templates |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28827.cs |
Added NUnit UI test that verifies group headers/footers don't appear when grouping is enabled on non-grouped collections (excluded from Windows due to unrelated issue #28824) |
| if (_groupSource[n] is IEnumerable list) | ||
| { | ||
| var source = ItemsSourceFactory.Create(list, _groupableItemsView, this); | ||
| source.HasFooter = _hasGroupFooters; | ||
| source.HasHeader = _hasGroupHeaders; | ||
| _groups.Add(source); | ||
| } |
There was a problem hiding this comment.
The fix only checks if the item is IEnumerable, but the iOS implementation in ObservableGroupedSource.cs (line 136) has a more restrictive check: if (_groupSource[n] is INotifyCollectionChanged && _groupSource[n] is IEnumerable list).
For consistency across platforms and to ensure proper observable behavior, consider adding the INotifyCollectionChanged check here as well:
if (_groupSource[n] is INotifyCollectionChanged && _groupSource[n] is IEnumerable list)
{
var source = ItemsSourceFactory.Create(list, _groupableItemsView, this);
source.HasFooter = _hasGroupFooters;
source.HasHeader = _hasGroupHeaders;
_groups.Add(source);
}This would ensure that only items implementing both interfaces are treated as observable groups, matching the iOS behavior.
| if (_groupSource[n] is IEnumerable list) | ||
| { | ||
| var source = ItemsSourceFactory.Create(list, _groupableItemsView, this); | ||
| source.HasFooter = _hasGroupFooters; | ||
| source.HasHeader = _hasGroupHeaders; | ||
| _groups.Add(source); | ||
| } |
There was a problem hiding this comment.
This PR fixes the DEPRECATED Items/ handlers (located at src/Controls/src/Core/Handlers/Items/), not the current Items2/ handlers.
According to the coding guidelines (CodingGuidelineID: 1000002), the Items/ handlers are deprecated and Items2/ is the active implementation. However, since Items2/ only has iOS implementation and Android CollectionView still uses Items/, this fix is appropriate for the current codebase.
Note: When Items2/ Android support is added in the future, this fix will need to be implemented there as well.
| @@ -0,0 +1,28 @@ | |||
| #if TEST_FAILS_ON_WINDOWS // NullReferenceException occurs when switching isGrouped to true | |||
There was a problem hiding this comment.
[nitpick] The test is excluded from Windows using #if TEST_FAILS_ON_WINDOWS with a reference to issue #28824, which indicates there's a separate NullReferenceException bug on Windows.
However, according to the PR description, the fix was tested on Windows (MacCatalyst checkbox is checked, which runs on macOS). The compilation directive name suggests the test itself fails on Windows, not that the fix doesn't work there.
Consider clarifying:
- Does the underlying bug (issue [Android] Group Header/Footer Repeated for All Items When IsGrouped is True for ObservableCollection #28827) affect Windows, or only the test infrastructure?
- If Windows has the same grouped collection bug, should a Windows-specific version of this test be created once issue [Windows] NullReferenceException thrown When Toggling IsGrouped to True in ObservableCollection Binding #28824 is resolved?
- Should the comment explain why Windows is excluded more clearly (e.g., "Test excluded on Windows due to unrelated NullReferenceException in test infrastructure - see [Windows] NullReferenceException thrown When Toggling IsGrouped to True in ObservableCollection Binding #28824")?
| #if TEST_FAILS_ON_WINDOWS // NullReferenceException occurs when switching isGrouped to true | |
| #if TEST_FAILS_ON_WINDOWS // Test excluded on Windows due to unrelated NullReferenceException in test infrastructure - see https://github.com/dotnet/maui/issues/28824. The underlying grouped collection bug (issue #28827) is not Windows-specific; re-enable this test on Windows once issue #28824 is resolved. |
…ctly Root cause: Three bugs caused the wrong comment format on PR #28886: 1. pr-finalize (Phase 2) overwrote the state file pr-XXX.md with its Verification Report, destroying all phase data (Pre-Flight, Tests, Gate) created by the PR Agent (Phase 1). 2. Phase 2 Copilot CLI session left the working tree dirty, causing skill files to go missing when Phase 3 started. 3. Phase 3 used a Copilot CLI session which, when skill files were missing, created its own broken script with wrong marker. Fixes: - pr-finalize now writes to pr-XXX-final.md (separate from main state) - git checkout -- . runs between Phase 1-2 and before Phase 3 - Phase 3 directly invokes post-ai-summary-comment.ps1 via pwsh instead of via Copilot CLI (deterministic, no hallucination risk) - Added GH_TOKEN to Run PR Reviewer step for comment posting
…ctly Root cause: Three bugs caused the wrong comment format on PR #28886: 1. pr-finalize (Phase 2) overwrote the state file pr-XXX.md with its Verification Report, destroying all phase data (Pre-Flight, Tests, Gate) created by the PR Agent (Phase 1). 2. Phase 2 Copilot CLI session left the working tree dirty, causing skill files to go missing when Phase 3 started. 3. Phase 3 used a Copilot CLI session which, when skill files were missing, created its own broken script with wrong marker. Fixes: - pr-finalize now writes to pr-XXX-final.md (separate from main state) - git checkout -- . runs between Phase 1-2 and before Phase 3 - Phase 3 directly invokes post-ai-summary-comment.ps1 via pwsh instead of via Copilot CLI (deterministic, no hallucination risk) - Added GH_TOKEN to Run PR Reviewer step for comment posting
…indows code as deprecated The instruction file was causing the Copilot code reviewer to incorrectly characterize Items/Android code as "DEPRECATED" when reviewing PR #28886. Items/ for Android and Windows is the ACTIVE and ONLY implementation - it is NOT deprecated. Only Items/ iOS/MacCatalyst code has been superseded by Items2/. Changes: - Replace deprecation-centric framing with active status framing - Add explicit Code Review Guidance section with correct/incorrect examples - Use "superseded" instead of "deprecated" for iOS/MacCatalyst specifics - Add the exact PR #28886 mistake as a common mistake to avoid Co-authored-by: PureWeen <5375137+PureWeen@users.noreply.github.com>
…ctly Root cause: Three bugs caused the wrong comment format on PR #28886: 1. pr-finalize (Phase 2) overwrote the state file pr-XXX.md with its Verification Report, destroying all phase data (Pre-Flight, Tests, Gate) created by the PR Agent (Phase 1). 2. Phase 2 Copilot CLI session left the working tree dirty, causing skill files to go missing when Phase 3 started. 3. Phase 3 used a Copilot CLI session which, when skill files were missing, created its own broken script with wrong marker. Fixes: - pr-finalize now writes to pr-XXX-final.md (separate from main state) - git checkout -- . runs between Phase 1-2 and before Phase 3 - Phase 3 directly invokes post-ai-summary-comment.ps1 via pwsh instead of via Copilot CLI (deterministic, no hallucination risk) - Added GH_TOKEN to Run PR Reviewer step for comment posting
…ctly Root cause: Three bugs caused the wrong comment format on PR #28886: 1. pr-finalize (Phase 2) overwrote the state file pr-XXX.md with its Verification Report, destroying all phase data (Pre-Flight, Tests, Gate) created by the PR Agent (Phase 1). 2. Phase 2 Copilot CLI session left the working tree dirty, causing skill files to go missing when Phase 3 started. 3. Phase 3 used a Copilot CLI session which, when skill files were missing, created its own broken script with wrong marker. Fixes: - pr-finalize now writes to pr-XXX-final.md (separate from main state) - git checkout -- . runs between Phase 1-2 and before Phase 3 - Phase 3 directly invokes post-ai-summary-comment.ps1 via pwsh instead of via Copilot CLI (deterministic, no hallucination risk) - Added GH_TOKEN to Run PR Reviewer step for comment posting
…ctly Root cause: Three bugs caused the wrong comment format on PR #28886: 1. pr-finalize (Phase 2) overwrote the state file pr-XXX.md with its Verification Report, destroying all phase data (Pre-Flight, Tests, Gate) created by the PR Agent (Phase 1). 2. Phase 2 Copilot CLI session left the working tree dirty, causing skill files to go missing when Phase 3 started. 3. Phase 3 used a Copilot CLI session which, when skill files were missing, created its own broken script with wrong marker. Fixes: - pr-finalize now writes to pr-XXX-final.md (separate from main state) - git checkout -- . runs between Phase 1-2 and before Phase 3 - Phase 3 directly invokes post-ai-summary-comment.ps1 via pwsh instead of via Copilot CLI (deterministic, no hallucination risk) - Added GH_TOKEN to Run PR Reviewer step for comment posting
…to all items when IsGrouped was true for an ObservableCollection (#28886) ### Root Cause of the issue - The items in the ItemsSource collection were being cast to IEnumerable and added to the groups collection, even when they were not actually of an IEnumerable type. As a result, these individual items were incorrectly treated as groups, leading to group-related behaviors such as the display of group headers and footers. ### Description of Change - I have added a type check to ensure that only items that implement IEnumerable are added to the groups collection. If an item is not an IEnumerable, it will no longer be treated as a group, and the groups collection will remain empty in such cases. ### Issues Fixed Fixes #28827 ### Tested the behaviour in the following platforms - [x] iOS - [x] Android - [ ] Windows - [x] Mac ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/5e7d5137-2e02-4cf3-8dfa-26e2ab6b197f"> | <video src="https://github.com/user-attachments/assets/97e19aa3-d379-49fe-b6ed-8ed6120019fd"> |
…to all items when IsGrouped was true for an ObservableCollection (#28886) ### Root Cause of the issue - The items in the ItemsSource collection were being cast to IEnumerable and added to the groups collection, even when they were not actually of an IEnumerable type. As a result, these individual items were incorrectly treated as groups, leading to group-related behaviors such as the display of group headers and footers. ### Description of Change - I have added a type check to ensure that only items that implement IEnumerable are added to the groups collection. If an item is not an IEnumerable, it will no longer be treated as a group, and the groups collection will remain empty in such cases. ### Issues Fixed Fixes #28827 ### Tested the behaviour in the following platforms - [x] iOS - [x] Android - [ ] Windows - [x] Mac ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/5e7d5137-2e02-4cf3-8dfa-26e2ab6b197f"> | <video src="https://github.com/user-attachments/assets/97e19aa3-d379-49fe-b6ed-8ed6120019fd"> |
…to all items when IsGrouped was true for an ObservableCollection (#28886) ### Root Cause of the issue - The items in the ItemsSource collection were being cast to IEnumerable and added to the groups collection, even when they were not actually of an IEnumerable type. As a result, these individual items were incorrectly treated as groups, leading to group-related behaviors such as the display of group headers and footers. ### Description of Change - I have added a type check to ensure that only items that implement IEnumerable are added to the groups collection. If an item is not an IEnumerable, it will no longer be treated as a group, and the groups collection will remain empty in such cases. ### Issues Fixed Fixes #28827 ### Tested the behaviour in the following platforms - [x] iOS - [x] Android - [ ] Windows - [x] Mac ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/5e7d5137-2e02-4cf3-8dfa-26e2ab6b197f"> | <video src="https://github.com/user-attachments/assets/97e19aa3-d379-49fe-b6ed-8ed6120019fd"> |
…to all items when IsGrouped was true for an ObservableCollection (#28886) ### Root Cause of the issue - The items in the ItemsSource collection were being cast to IEnumerable and added to the groups collection, even when they were not actually of an IEnumerable type. As a result, these individual items were incorrectly treated as groups, leading to group-related behaviors such as the display of group headers and footers. ### Description of Change - I have added a type check to ensure that only items that implement IEnumerable are added to the groups collection. If an item is not an IEnumerable, it will no longer be treated as a group, and the groups collection will remain empty in such cases. ### Issues Fixed Fixes #28827 ### Tested the behaviour in the following platforms - [x] iOS - [x] Android - [ ] Windows - [x] Mac ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/5e7d5137-2e02-4cf3-8dfa-26e2ab6b197f"> | <video src="https://github.com/user-attachments/assets/97e19aa3-d379-49fe-b6ed-8ed6120019fd"> |
…to all items when IsGrouped was true for an ObservableCollection (#28886) ### Root Cause of the issue - The items in the ItemsSource collection were being cast to IEnumerable and added to the groups collection, even when they were not actually of an IEnumerable type. As a result, these individual items were incorrectly treated as groups, leading to group-related behaviors such as the display of group headers and footers. ### Description of Change - I have added a type check to ensure that only items that implement IEnumerable are added to the groups collection. If an item is not an IEnumerable, it will no longer be treated as a group, and the groups collection will remain empty in such cases. ### Issues Fixed Fixes #28827 ### Tested the behaviour in the following platforms - [x] iOS - [x] Android - [ ] Windows - [x] Mac ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/5e7d5137-2e02-4cf3-8dfa-26e2ab6b197f"> | <video src="https://github.com/user-attachments/assets/97e19aa3-d379-49fe-b6ed-8ed6120019fd"> |
…to all items when IsGrouped was true for an ObservableCollection (#28886) ### Root Cause of the issue - The items in the ItemsSource collection were being cast to IEnumerable and added to the groups collection, even when they were not actually of an IEnumerable type. As a result, these individual items were incorrectly treated as groups, leading to group-related behaviors such as the display of group headers and footers. ### Description of Change - I have added a type check to ensure that only items that implement IEnumerable are added to the groups collection. If an item is not an IEnumerable, it will no longer be treated as a group, and the groups collection will remain empty in such cases. ### Issues Fixed Fixes #28827 ### Tested the behaviour in the following platforms - [x] iOS - [x] Android - [ ] Windows - [x] Mac ### Screenshot | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/5e7d5137-2e02-4cf3-8dfa-26e2ab6b197f"> | <video src="https://github.com/user-attachments/assets/97e19aa3-d379-49fe-b6ed-8ed6120019fd"> |
## What's Coming .NET MAUI inflight/candidate introduces significant improvements across all platforms with focus on quality, performance, and developer experience. This release includes 24 commits with various improvements, bug fixes, and enhancements. ## Animation - [Android] Fixed TransformProperties issue when a wrapper view is present by @Ahamed-Ali in #29228 <details> <summary>🔧 Fixes</summary> - [Android Image.Scale produces wrong layout](#7432) </details> ## Button - Fix ImageButton not rendering correctly based on its bounds by @Shalini-Ashokan in #28309 <details> <summary>🔧 Fixes</summary> - [ImageButton dosen't scale Image correctly](#25558) - [ButtonImage width not sizing correctly](#14346) </details> ## CollectionView - [Android] Fixed issue where group Header/Footer template was applied to all items when IsGrouped was true for an ObservableCollection by @Tamilarasan-Paranthaman in #28886 <details> <summary>🔧 Fixes</summary> - [[Android] Group Header/Footer Repeated for All Items When IsGrouped is True for ObservableCollection](#28827) </details> - [Android] CollectionView: Fix reordering when using DataTemplateSelector by @NanthiniMahalingam in #32349 <details> <summary>🔧 Fixes</summary> - [[Android][.NET9] CollectionView Reorderer doesn't work when using TemplateSelector](#32223) </details> - [Android] Fix for incorrect scroll position when using ScrollTo with a header in CollectionView by @SyedAbdulAzeemSF4852 in #30966 <details> <summary>🔧 Fixes</summary> - [Potential off-by-one error when using ScrollTo in CollectionView with a header.](#18389) </details> - Fix Incorrect Scrolling Behavior in CollectionView ScrollTo Method Using Index Value by @Shalini-Ashokan in #27246 <details> <summary>🔧 Fixes</summary> - [CollectionView ScrollTo not working under android](#27117) </details> - [Android] Fix System.IndexOutOfRangeException when scrolling CollectionView with image CarouselView by @devanathan-vaithiyanathan in #31722 <details> <summary>🔧 Fixes</summary> - [System.IndexOutOfRangeException when scrolling CollectionView with image CarouselView](#31680) </details> - [Android] Fix VerticalOffset Update When Modifying CollectionView.ItemsSource While Scrolled by @devanathan-vaithiyanathan in #26782 <details> <summary>🔧 Fixes</summary> - [CollectionView.Scrolled event offset isn't correctly reset when items change on Android](#21708) </details> ## Editor - Fixed Editor vertical text alignment not working after toggling IsVisible by @NanthiniMahalingam in #26194 <details> <summary>🔧 Fixes</summary> - [Editor vertical text alignment not working after toggling IsVisible](#25973) </details> ## Entry - [Android] Fix Numeric Entry not accepting the appropriate Decimal Separator by @devanathan-vaithiyanathan in #27376 <details> <summary>🔧 Fixes</summary> - [Numeric Entry uses wrong decimal separator in MAUI app running on Android](#17152) </details> - [Android & iOS] Entry/Editor: Dismiss keyboard when control becomes invisible by @prakashKannanSf3972 in #27340 <details> <summary>🔧 Fixes</summary> - [android allows type into hidden Entry control](#27236) </details> ## Gestures - [Android] Fixed PointerGestureRecognizer not triggering PointerMoved event by @KarthikRajaKalaimani in #33889 <details> <summary>🔧 Fixes</summary> - [PointerGestureRecognizer does not fire off PointerMove event on Android](#33690) </details> - [Android] Fix PointerMoved and PointerReleased not firing in PointerGestureRecognizer by @KarthikRajaKalaimani in #34209 <details> <summary>🔧 Fixes</summary> - [PointerGestureRecognizer does not fire off PointerMove event on Android](#33690) </details> ## Navigation - [Android] Shell: Fix OnBackButtonPressed not firing for navigation bar back button by @kubaflo in #33531 <details> <summary>🔧 Fixes</summary> - [OnBackButtonPressed not firing for Shell Navigation Bar button in .NET 10 SR2](#33523) </details> ## Shell - [iOS] Fixed Shell Navigating event showing same current and target values by @Vignesh-SF3580 in #25749 <details> <summary>🔧 Fixes</summary> - [OnNavigating wrong target when tapping the same tab](#25599) </details> - [iOS, macOS] Fixed Shell Flyout Icon is always black in iOS 26 by @Dhivya-SF4094 in #32997 <details> <summary>🔧 Fixes</summary> - [Shell Flyout Icon is always black](#32867) - [[iOS] Color Not Applied to Flyout Icon or Title on iOS 26](#33971) </details> ## TitleView - [Android] Fixed duplicate title icon when setting TitleIconImageSource Multiple times by @SubhikshaSf4851 in #31487 <details> <summary>🔧 Fixes</summary> - [[Android] Duplicate Title Icon Appears When Setting NavigationPage.TitleIconImageSource Multiple Times](#31445) </details> ## WebView - Fixed the crash on iOS when setting HeightRequest on WebView inside a ScrollView with IsVisible set to false by @Ahamed-Ali in #29022 <details> <summary>🔧 Fixes</summary> - [Specifying HeightRequest in Webview when wrapped by ScrollView set "invisible" causes crash in iOS](#26795) </details> <details> <summary>🧪 Testing (3)</summary> - [Testing] Fix for enable uitests ios26 by @TamilarasanSF4853 in #33686 - [Testing] Fixed Test case failure in PR 34173 - [02/21/2026] Candidate - 1 by @TamilarasanSF4853 in #34192 - [Testing] Fixed Test case failure in PR 34173 - [02/21/2026] Candidate - 2 by @TamilarasanSF4853 in #34233 </details> <details> <summary>📦 Other (3)</summary> - Fix Glide IllegalArgumentException in PlatformInterop for destroyed activities by @jonathanpeppers via @Copilot in #33805 - [iOS] Fix MauiCALayer and StaticCAShapeLayer crash on finalizer thread by @pshoey in #33818 <details> <summary>🔧 Fixes</summary> - [[iOS] MauiCALayer and StaticCAShapeLayer crash on finalizer thread in Release/AOT builds](#33800) </details> - Merge branch 'main' into inflight/candidate in 1a00f12 </details> **Full Changelog**: main...inflight/candidate --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> Co-authored-by: pshoey <pshoey@users.noreply.github.com> Co-authored-by: Subhiksha Chandrasekaran <subhiksha.c@syncfusion.com> Co-authored-by: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Co-authored-by: prakashKannanSf3972 <127308739+prakashKannanSf3972@users.noreply.github.com> Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com> Co-authored-by: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com> Co-authored-by: NanthiniMahalingam <105482474+NanthiniMahalingam@users.noreply.github.com> Co-authored-by: BagavathiPerumal <bagavathiperumal.a@syncfusion.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com> Co-authored-by: Shalini-Ashokan <shalini.ashokan@syncfusion.com> Co-authored-by: Tamilarasan Paranthaman <93904422+Tamilarasan-Paranthaman@users.noreply.github.com> Co-authored-by: SyedAbdulAzeemSF4852 <syedabdulazeem.a@syncfusion.com> Co-authored-by: Ahamed-Ali <102580874+Ahamed-Ali@users.noreply.github.com> Co-authored-by: Dhivya-SF4094 <127717131+Dhivya-SF4094@users.noreply.github.com> Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com> Co-authored-by: Matthew Leibowitz <mattleibow@live.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: TamilarasanSF4853 <tamilarasan.velu@syncfusion.com>
…tems when IsGrouped was true for an ObservableCollection (#29144) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Issue 1: Group header and footer templates not updating correctly at runtime on iOS. ### Root Cause The mapper for `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` in `GroupableItemsViewHandler` was conditionally compiled with `#if WINDOWS || __ANDROID__ || TIZEN`, meaning it was excluded from iOS builds. As a result, changing the template at runtime on iOS had no effect and templates were never displayed. ### Description of Change Removed the `#if WINDOWS || __ANDROID__ || TIZEN` preprocessor guard from `GroupableItemsViewHandler.cs`, making the `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` mappers active on all platforms including iOS. Both mappers call `MapIsGrouped`, which triggers `UpdateItemsSource()` and refreshes the grouping state. --- ## Issue 2: Group header/footer templates incorrectly applied to all items in a flat ObservableCollection when `IsGrouped = true`. ### Root Cause In `ObservableGroupedSource.cs` (iOS), the `GroupsCount()` method iterated over all items in `_groupSource` and counted every item, regardless of whether it was an `IEnumerable` (i.e., an actual group). When `IsGrouped = true` but the source was a flat `ObservableCollection<T>` (non-grouped), each non-grouped item was counted as a section, causing `NumberOfSections` to be inflated. This led to header and footer templates being incorrectly applied to every item. ### Description of Change Modified `GroupsCount()` to only increment the count for items that implement `IEnumerable`. Non-`IEnumerable` items are no longer counted as sections. As a result, `NumberOfSections` now correctly reflects the number of actual groups, preventing header/footer templates from appearing for non-grouped items. --- ### Issues Fixed Fixes #29141 ### Test Case Tests for this fix are included in this PR: - `src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs` — HostApp page with a `CollectionView` bound to a flat `ObservableCollection`, with radio buttons to toggle `IsGrouped`, `GroupHeaderTemplate`, and `GroupFooterTemplate` at runtime. - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs` — NUnit UI test verifying that group header/footer template views are NOT shown when the source collection is not grouped. > **Note:** The test is currently excluded from Windows (unrelated NullReferenceException — see #28824) and Android (separate fix in PR #28886). It runs on iOS and MacCatalyst. ### Platforms Tested - [x] iOS - [x] Android - [x] Mac - [ ] Windows </details> --------- Co-authored-by: Shane Neuville <5375137+PureWeen@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com>
…tems when IsGrouped was true for an ObservableCollection (#29144) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Issue 1: Group header and footer templates not updating correctly at runtime on iOS. ### Root Cause The mapper for `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` in `GroupableItemsViewHandler` was conditionally compiled with `#if WINDOWS || __ANDROID__ || TIZEN`, meaning it was excluded from iOS builds. As a result, changing the template at runtime on iOS had no effect and templates were never displayed. ### Description of Change Removed the `#if WINDOWS || __ANDROID__ || TIZEN` preprocessor guard from `GroupableItemsViewHandler.cs`, making the `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` mappers active on all platforms including iOS. Both mappers call `MapIsGrouped`, which triggers `UpdateItemsSource()` and refreshes the grouping state. --- ## Issue 2: Group header/footer templates incorrectly applied to all items in a flat ObservableCollection when `IsGrouped = true`. ### Root Cause In `ObservableGroupedSource.cs` (iOS), the `GroupsCount()` method iterated over all items in `_groupSource` and counted every item, regardless of whether it was an `IEnumerable` (i.e., an actual group). When `IsGrouped = true` but the source was a flat `ObservableCollection<T>` (non-grouped), each non-grouped item was counted as a section, causing `NumberOfSections` to be inflated. This led to header and footer templates being incorrectly applied to every item. ### Description of Change Modified `GroupsCount()` to only increment the count for items that implement `IEnumerable`. Non-`IEnumerable` items are no longer counted as sections. As a result, `NumberOfSections` now correctly reflects the number of actual groups, preventing header/footer templates from appearing for non-grouped items. --- ### Issues Fixed Fixes #29141 ### Test Case Tests for this fix are included in this PR: - `src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs` — HostApp page with a `CollectionView` bound to a flat `ObservableCollection`, with radio buttons to toggle `IsGrouped`, `GroupHeaderTemplate`, and `GroupFooterTemplate` at runtime. - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs` — NUnit UI test verifying that group header/footer template views are NOT shown when the source collection is not grouped. > **Note:** The test is currently excluded from Windows (unrelated NullReferenceException — see #28824) and Android (separate fix in PR #28886). It runs on iOS and MacCatalyst. ### Platforms Tested - [x] iOS - [x] Android - [x] Mac - [ ] Windows </details> --------- Co-authored-by: Shane Neuville <5375137+PureWeen@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com>
…tems when IsGrouped was true for an ObservableCollection (#29144) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Issue 1: Group header and footer templates not updating correctly at runtime on iOS. ### Root Cause The mapper for `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` in `GroupableItemsViewHandler` was conditionally compiled with `#if WINDOWS || __ANDROID__ || TIZEN`, meaning it was excluded from iOS builds. As a result, changing the template at runtime on iOS had no effect and templates were never displayed. ### Description of Change Removed the `#if WINDOWS || __ANDROID__ || TIZEN` preprocessor guard from `GroupableItemsViewHandler.cs`, making the `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` mappers active on all platforms including iOS. Both mappers call `MapIsGrouped`, which triggers `UpdateItemsSource()` and refreshes the grouping state. --- ## Issue 2: Group header/footer templates incorrectly applied to all items in a flat ObservableCollection when `IsGrouped = true`. ### Root Cause In `ObservableGroupedSource.cs` (iOS), the `GroupsCount()` method iterated over all items in `_groupSource` and counted every item, regardless of whether it was an `IEnumerable` (i.e., an actual group). When `IsGrouped = true` but the source was a flat `ObservableCollection<T>` (non-grouped), each non-grouped item was counted as a section, causing `NumberOfSections` to be inflated. This led to header and footer templates being incorrectly applied to every item. ### Description of Change Modified `GroupsCount()` to only increment the count for items that implement `IEnumerable`. Non-`IEnumerable` items are no longer counted as sections. As a result, `NumberOfSections` now correctly reflects the number of actual groups, preventing header/footer templates from appearing for non-grouped items. --- ### Issues Fixed Fixes #29141 ### Test Case Tests for this fix are included in this PR: - `src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs` — HostApp page with a `CollectionView` bound to a flat `ObservableCollection`, with radio buttons to toggle `IsGrouped`, `GroupHeaderTemplate`, and `GroupFooterTemplate` at runtime. - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs` — NUnit UI test verifying that group header/footer template views are NOT shown when the source collection is not grouped. > **Note:** The test is currently excluded from Windows (unrelated NullReferenceException — see #28824) and Android (separate fix in PR #28886). It runs on iOS and MacCatalyst. ### Platforms Tested - [x] iOS - [x] Android - [x] Mac - [ ] Windows </details> --------- Co-authored-by: Shane Neuville <5375137+PureWeen@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com>
…tems when IsGrouped was true for an ObservableCollection (#29144) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Issue 1: Group header and footer templates not updating correctly at runtime on iOS. ### Root Cause The mapper for `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` in `GroupableItemsViewHandler` was conditionally compiled with `#if WINDOWS || __ANDROID__ || TIZEN`, meaning it was excluded from iOS builds. As a result, changing the template at runtime on iOS had no effect and templates were never displayed. ### Description of Change Removed the `#if WINDOWS || __ANDROID__ || TIZEN` preprocessor guard from `GroupableItemsViewHandler.cs`, making the `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` mappers active on all platforms including iOS. Both mappers call `MapIsGrouped`, which triggers `UpdateItemsSource()` and refreshes the grouping state. --- ## Issue 2: Group header/footer templates incorrectly applied to all items in a flat ObservableCollection when `IsGrouped = true`. ### Root Cause In `ObservableGroupedSource.cs` (iOS), the `GroupsCount()` method iterated over all items in `_groupSource` and counted every item, regardless of whether it was an `IEnumerable` (i.e., an actual group). When `IsGrouped = true` but the source was a flat `ObservableCollection<T>` (non-grouped), each non-grouped item was counted as a section, causing `NumberOfSections` to be inflated. This led to header and footer templates being incorrectly applied to every item. ### Description of Change Modified `GroupsCount()` to only increment the count for items that implement `IEnumerable`. Non-`IEnumerable` items are no longer counted as sections. As a result, `NumberOfSections` now correctly reflects the number of actual groups, preventing header/footer templates from appearing for non-grouped items. --- ### Issues Fixed Fixes #29141 ### Test Case Tests for this fix are included in this PR: - `src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs` — HostApp page with a `CollectionView` bound to a flat `ObservableCollection`, with radio buttons to toggle `IsGrouped`, `GroupHeaderTemplate`, and `GroupFooterTemplate` at runtime. - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs` — NUnit UI test verifying that group header/footer template views are NOT shown when the source collection is not grouped. > **Note:** The test is currently excluded from Windows (unrelated NullReferenceException — see #28824) and Android (separate fix in PR #28886). It runs on iOS and MacCatalyst. ### Platforms Tested - [x] iOS - [x] Android - [x] Mac - [ ] Windows </details> --------- Co-authored-by: Shane Neuville <5375137+PureWeen@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com>
…tems when IsGrouped was true for an ObservableCollection (#29144) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Issue 1: Group header and footer templates not updating correctly at runtime on iOS. ### Root Cause The mapper for `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` in `GroupableItemsViewHandler` was conditionally compiled with `#if WINDOWS || __ANDROID__ || TIZEN`, meaning it was excluded from iOS builds. As a result, changing the template at runtime on iOS had no effect and templates were never displayed. ### Description of Change Removed the `#if WINDOWS || __ANDROID__ || TIZEN` preprocessor guard from `GroupableItemsViewHandler.cs`, making the `GroupFooterTemplateProperty` and `GroupHeaderTemplateProperty` mappers active on all platforms including iOS. Both mappers call `MapIsGrouped`, which triggers `UpdateItemsSource()` and refreshes the grouping state. --- ## Issue 2: Group header/footer templates incorrectly applied to all items in a flat ObservableCollection when `IsGrouped = true`. ### Root Cause In `ObservableGroupedSource.cs` (iOS), the `GroupsCount()` method iterated over all items in `_groupSource` and counted every item, regardless of whether it was an `IEnumerable` (i.e., an actual group). When `IsGrouped = true` but the source was a flat `ObservableCollection<T>` (non-grouped), each non-grouped item was counted as a section, causing `NumberOfSections` to be inflated. This led to header and footer templates being incorrectly applied to every item. ### Description of Change Modified `GroupsCount()` to only increment the count for items that implement `IEnumerable`. Non-`IEnumerable` items are no longer counted as sections. As a result, `NumberOfSections` now correctly reflects the number of actual groups, preventing header/footer templates from appearing for non-grouped items. --- ### Issues Fixed Fixes #29141 ### Test Case Tests for this fix are included in this PR: - `src/Controls/tests/TestCases.HostApp/Issues/Issue29141.cs` — HostApp page with a `CollectionView` bound to a flat `ObservableCollection`, with radio buttons to toggle `IsGrouped`, `GroupHeaderTemplate`, and `GroupFooterTemplate` at runtime. - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29141.cs` — NUnit UI test verifying that group header/footer template views are NOT shown when the source collection is not grouped. > **Note:** The test is currently excluded from Windows (unrelated NullReferenceException — see #28824) and Android (separate fix in PR #28886). It runs on iOS and MacCatalyst. ### Platforms Tested - [x] iOS - [x] Android - [x] Mac - [ ] Windows </details> --------- Co-authored-by: Shane Neuville <5375137+PureWeen@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com>
Root Cause of the issue
Description of Change
Issues Fixed
Fixes #28827
Tested the behaviour in the following platforms
Screenshot
Before-Fix.mov
After-Fix.mov