Skip to content

[Windows] Fix Indicator View Causing Looping Issue with CarouselView#27880

Closed
Tamilarasan-Paranthaman wants to merge 122 commits into
dotnet:inflight/currentfrom
Tamilarasan-Paranthaman:fix-27563
Closed

[Windows] Fix Indicator View Causing Looping Issue with CarouselView#27880
Tamilarasan-Paranthaman wants to merge 122 commits into
dotnet:inflight/currentfrom
Tamilarasan-Paranthaman:fix-27563

Conversation

@Tamilarasan-Paranthaman

@Tamilarasan-Paranthaman Tamilarasan-Paranthaman commented Feb 18, 2025

Copy link
Copy Markdown
Member

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 from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause of the issue

  • In the UpdateCurrentItem method, the view scrolls to the current item position. When the item position is updated from the IndicatorView, it triggers the Scrolled event, which then updates the position based on the current index. In the UpdatePosition method, the current item is continuously updated even while the view is still in a scrolling state, leading to an infinite scrolling loop and other position related issues.

Description of Change

  • Introduced a _lastScrolledToPosition tracking field to prevent re-entrant ScrollTo calls that cause the infinite loop.
  • Modified UpdateCurrentItem to only issue ScrollTo when the position actually differs AND has not already been scrolled to.
  • Added ScrollTo in UpdatePosition (guarded by IsDragging/IsScrolling checks) so IndicatorView-driven position changes scroll the carousel correctly.
  • Reset the tracker on user scrolling (CarouselScrolled) and collection source changes (OnCollectionItemsSourceChanged).
  • Removed the Loop centering code from UpdateInitialPosition (see code review note about potential regression).

Why tests are restricted on Windows and Mac:

  • The test added in this PR for Issue27563 is currently restricted on Windows and MacCatalyst because it relies on Appium-driven CarouselView swipe/position behavior, which is not reliable on these platforms.
  • For Windows, this aligns with the existing issue tracked in [Testing] Appium test does not perform the tap action with CarouselView. #29245, where CarouselView automation leads to timeouts and failures unrelated to the fix. Once this issue is resolved, we can remove the platform restriction.
  • Keeping the test behind platform guards helps avoid introducing known UI test flakiness into the PR.

Issues Fixed

Fixes #27563
Fixes #22468
Fixes #7149

Tested the behaviour in the following platforms

  • Windows
  • Android
  • iOS
  • Mac

Screenshot

Before Issue Fix After Issue Fix
Before-Fix.mp4
After-Fix.mp4

@dotnet-policy-service dotnet-policy-service Bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Feb 18, 2025
@jsuarezruiz jsuarezruiz added the area-controls-collectionview CollectionView, CarouselView, IndicatorView label Feb 18, 2025
@Tamilarasan-Paranthaman Tamilarasan-Paranthaman marked this pull request as ready for review February 24, 2025 13:32
@Tamilarasan-Paranthaman Tamilarasan-Paranthaman requested a review from a team as a code owner February 24, 2025 13:32
@jsuarezruiz

Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz

Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz

Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).


[Test]
[Category(UITestCategories.CarouselView)]
public void VerifyCarouselViewScrolling()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The test is failing on Mac and Windows:
image

   at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2367
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2384
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 665
   at Microsoft.Maui.TestCases.Tests.Issues.Issue27563.VerifyCarouselViewScrolling() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27563.cs:line 21
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The test failed with a timeout exception on both Windows and Mac.

On Mac, this appears to be due to the lack of support for SwipeRightToLeft. We can modify the test case specifically for the Mac platform.

On Windows, the test ran for an extended period before failing with a timeout exception.

I conducted further testing on a local machine and found that the test passed when CarouselView.Loop was set to false. This suggests that the issue occurs only when CarouselView.Loop is set to true. Additionally, I identified other test cases where Loop is enabled, and they also include the TEST_FAILS_ON_WINDOWS condition referencing this issue (e.g., Script: 12574 - Sample - 12574).

However, in the CI environment, the failure when Loop is enabled appears to be due to a different issue.

@jsuarezruiz, could you please share your insights on this and suggest how we should proceed on Windows platforms?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@jsuarezruiz @Tamilarasan-Paranthaman is there a way forward with this PR?

@bronteq

bronteq commented Jul 22, 2025

Copy link
Copy Markdown

@jsuarezruiz friendly ping

@jsuarezruiz

Copy link
Copy Markdown
Contributor

/rebase

Copilot AI review requested due to automatic review settings October 31, 2025 12:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jsuarezruiz

Copy link
Copy Markdown
Contributor

Rebased and running a new build.

Comment thread src/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cs

[Test]
[Category(UITestCategories.CarouselView)]
public void VerifyCarouselViewScrolling()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can extend the new test to cover loop initial centering and the reentrancy case (tap indicator repeatedly while swiping), and a test that toggles Position to the same value to ensure no scroll storm occurs.

@Tamilarasan-Paranthaman Tamilarasan-Paranthaman marked this pull request as draft December 3, 2025 12:33
@kubaflo kubaflo added the stale Indicates a stale issue/pr and will be closed soon label Mar 8, 2026
@kubaflo

kubaflo commented Mar 8, 2026

Copy link
Copy Markdown
Contributor

Hi, is this still valid?

@github-actions

github-actions Bot commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 27880

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 27880"

@Vignesh-SF3580

Copy link
Copy Markdown
Contributor

Hi, is this still valid?

@kubaflo I have verified this on my end, and it is still valid PR. The issue still reproduces on the latest main source and is resolved by the changes made in this PR.

Without Fix With Fix
WithoutFix27880.mp4
WithFix27880.mp4

Qythyx and others added 2 commits April 14, 2026 14:02
…4936)

## Summary

- For `Default` and `Fixed` `FlyoutHeaderBehavior`, the flyout scroll
view is now positioned below the header instead of overlapping it with a
content inset. This prevents items from rendering behind
semi-transparent headers when scrolling.
- `Scroll` and `CollapseOnScroll` behaviors are unchanged — they still
overlap the header so it can scroll away or shrink.
- Adds regression test
`FlyoutScrollViewDoesNotOverlapHeaderForDefaultAndFixed` and updates
`FlyoutHeaderContentAndFooterAllMeasureCorrectly` to match the new
layout for Default/Fixed.

Fixes dotnet#34925

## Changes

**`ShellFlyoutLayoutManager.cs`** (iOS):
- `SetHeaderContentInset()`: For Default/Fixed, sets `ContentInset.Top =
0` since the scroll view frame already starts below the header.
- `LayoutContent()`: For Default/Fixed, adds the full header height to
the content Y offset instead of just the margin.

**`ShellFlyoutTests.cs`**:
- New test: `FlyoutScrollViewDoesNotOverlapHeaderForDefaultAndFixed` —
verifies the scroll view frame starts at or below the header bottom for
Default and Fixed behaviors.
- Updated test: `FlyoutHeaderContentAndFooterAllMeasureCorrectly` —
adjusts iOS ScrollView expectations so that only Scroll/CollapseOnScroll
use content inset; Default/Fixed use frame positioning.

## Test plan

- [x] New regression test
`FlyoutScrollViewDoesNotOverlapHeaderForDefaultAndFixed` fails without
fix, passes with fix
- [x] Existing `FlyoutHeaderContentAndFooterAllMeasureCorrectly` test
passes with updated expectations
- [ ] Manual verification: open flyout with semi-transparent header and
15+ items, scroll down — items should not be visible behind header

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------
…t#28071)

<!-- 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!


<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
When a Margin value is set for the Path, it renders correctly. However,
when the Path is placed inside a StackLayout, it does not render.

### Root Cause
While measuring the Path, the size calculation does not account for the
Margin value, which leads to incorrect path rendering.

### Description of Change
To include the Margin value in size calculations and ensure proper Path
rendering.

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
 
### Issues Fixed
  
Fixes dotnet#13801 

### Output  ScreenShot

| Before  | After  |
|---------|--------|
| <img width="501" alt="image (2)"
src="https://github.com/user-attachments/assets/7afdacbd-089b-47c2-bad9-216a19618bca"
/> | <img width="494" alt="image (3)"
src="https://github.com/user-attachments/assets/c64f2eeb-b9c0-4340-beb6-f35fd63f28fc"
/> |
Dhivya-SF4094 and others added 13 commits April 15, 2026 11:51
…hicsView (dotnet#34557)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
### Issue Details
The FlowDirection property is not functioning as expected when applied
to Drawable controls and GraphicsView.
When FlowDirection is set to either RightToLeft or LeftToRight, there is
no observable change in layout behavior.

### Root Cause
ShapeViewHandler had no FlowDirection mapper, and ShapeDrawable.Draw()
never applied canvas mirroring.

### Description of Change
**Android**: Updated PlatformGraphicsView to mirror its content
horizontally when the layout direction is RTL by applying a translation
and scale transformation in the Draw method.
**iOS**: Modified PlatformGraphicsView to check
EffectiveUserInterfaceLayoutDirection and apply a horizontal flip
transformation when in RTL mode.
**Windows**: Changed PlatformGraphicsView to concatenate a scale and
translation transform for RTL flow direction before drawing content.

### Validated the behaviour in the following platforms
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac

### Issues Fixed:
Fixes dotnet#34402 

### Screenshots
| Before  | After |
|---------|--------|
|  <video
src="https://github.com/user-attachments/assets/62bec2c7-dbe5-4696-9f78-d2d1b4bcdaec">
|   <video
src="https://github.com/user-attachments/assets/d143819d-b525-455b-adeb-d589171de61e"> 
|
…t#34954)

<!-- 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!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details

- HEIC images picked via PickPhotosAsync are not displayed — the image
appears blank, whereas other image formats (e.g., JPEG, PNG) display
correctly.

### Root Cause of the issue

- PR [34250](dotnet#34250) moved
CompletedHandler invocation into the DismissViewController completion
callback. This introduced a GC race condition where
PhotoPickerPresentationControllerDelegate.Dispose() fires
tcs.TrySetResult([]) before the async CompletedHandler finishes HEIC
transcoding.
- HEIC is particularly affected because
NSItemProvider.LoadDataRepresentationAsync transcoding is significantly
slower than JPEG/PNG loading, widening the GC window.

### Description of Change
**Race condition prevention:**

* In `PhotoPickerDelegate.DidFinishPicking`, the `Handler` property of
`PhotoPickerPresentationControllerDelegate` is set to `null` before
dismissing the picker to avoid a garbage collection race condition that
could interfere with the async completion handler, especially during
slow operations like HEIC transcoding.
<!-- Enter description of the fix in this section -->

### Issues Fixed
Fixes dotnet#34953

### Tested the behaviour in the following platforms

- [ ] - Windows 
- [ ] - Android
- [x] - iOS
- [ ] - Mac

| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/368192e4-57ea-4732-82df-3e3ca386ab35">
| <video
src="https://github.com/user-attachments/assets/0e3a5066-e092-4461-9199-1730475307d8">
|

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
…ion on the second programmatic call (dotnet#34982)

<!-- 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 Details

The issue occurs when calling the SwipeView.Open() method
programmatically multiple times on iOS and MacCatalyst platforms. The
first call to open swipe items (such as RightItems or BottomItems) works
as expected, but the second call throws a System.ArgumentException with
the message “An item with the same key has already been added.”
 
This behavior is specific to certain swipe directions that rely on
negative offsets (such as right and bottom swipe actions), where the
swipe interaction briefly appears and then resets unexpectedly. As a
result, internal state inconsistencies lead to a crash during subsequent
calls.

### Root Cause

The issue occur because of an incorrect use of Math.Abs on the
_swipeOffset value inside the ProgrammaticallyOpenSwipeItem()method.
This operation removes the negative sign required for specific swipe
directions (such as left or up), causing the offset to become invalid
during layout validation.
 
Due to this invalid offset, the swipe view resets to a closed state
while still retaining previously added entries in the _swipeItems
dictionary. When Open() is called again, the same keys are added again
to the dictionary, resulting in a duplicate key exception.

### Description of Change

The fix involves removing the Math.Abs operation on _swipeOffset to
preserve the correct directional value required for swipe behavior,
ensuring that the swipe state remains consistent after layout
validation.
 
Additionally, a defensive improvement is introduced by clearing the
_swipeItems dictionary before repopulating it in the UpdateSwipeItems()
method. This prevents duplicate key insertion and ensures that the
method behaves safely even if invoked multiple times under unexpected
conditions.

### Issues Fixed
Fixes dotnet#34917

### Validated the behaviour in the following platforms

- [ ] Windows
- [ ] Android
- [x] iOS
- [x] Mac

### Output
| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/55a131cb-6ccc-4776-80d7-90655651565d">
| <video
src="https://github.com/user-attachments/assets/5efc010e-04ec-4e4d-8729-d43f41d2d6bf">
|
…ng a NonFlyOutPage (dotnet#34839)

<!-- 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 Details:

Title of FlyOutPage is not updating anymore after showing a
NonFlyOutPage in Android Platform.
       
### Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage,
the Window.Page setter clears flyoutPage.Parent = null synchronously
before propagating the Window=null event through the element hierarchy.
This means by the time NavigationPage.OnWindowChanged(null) fires to
clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates
to false (Parent is already null). As a result, flyoutPage.Toolbar =
null never executes — the stale, now-disconnected NavigationPageToolbar
remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page,
FindMyToolbar() traverses the ancestors, finds the stale toolbar on
FlyoutPage.Toolbar, and returns early — no new connected toolbar is
created. Since the stale toolbar's ToolbarTracker subscriptions were
severed by Disconnect(), it never receives CurrentPage change
notifications, so the title is permanently frozen.

### Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in
NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining
check flyoutPage.Toolbar == _toolbar is the correct and sufficient
invariant — it ensures we only clear the toolbar that this
NavigationPage created. The Parent is IWindow guard was redundant in the
normal case (when FlyoutPage is the root page, Parent is IWindow), but
fatally incorrect during a Window.Page swap because the ordering
guarantee it depended on didn't hold.

**Tested the behavior in the following platforms:**

- [x] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  dotnet#33615         

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/00869428-8cb4-43a8-981b-7ac6b018e184"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/81f94d6d-8c25-4d08-a699-4b3db32e76c8"
Width="300" Height="600"> |
…otnet#34970)

<!-- Please keep the note below for people who 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 whether this change resolves your
issue. Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

This pull request addresses the issue where the `DatePicker` control on
MacCatalyst did not correctly raise its `Opened` and `Closed` events.
The changes implement a more robust mechanism for detecting when the
DatePicker is opened and closed, specifically for MacCatalyst, and add
new tests to verify this behavior.
### Description of Change :

**MacCatalyst DatePicker Event Handling Improvements:**

* Added logic to traverse the internal view hierarchy of the compact
`UIDatePicker` to find and wire up `UITextField` subviews, allowing
detection of when the DatePicker is opened via the `EditingDidBegin`
event. A one-shot observer is registered to detect when the picker
popover window closes, ensuring the `Closed` event is raised reliably.
(`DatePickerHandler.MacCatalyst.cs`)
* Implemented cleanup logic to unwire event handlers and remove
observers during disconnect, preventing memory leaks and spurious event
firing. (`DatePickerHandler.MacCatalyst.cs`)
* Removed previous event handler attachments
(`EditingDidBegin`/`EditingDidEnd`) from the proxy, as the new mechanism
supersedes them. (`DatePickerHandler.MacCatalyst.cs`)
[[1]](diffhunk://#diff-2107542f6f788907263db46eab6a80232ed765aa515806945e8f65681c8421d1L105-L113)
[[2]](diffhunk://#diff-2107542f6f788907263db46eab6a80232ed765aa515806945e8f65681c8421d1L125-L136)

**Testing Enhancements:**

* Added a new test case page (`Issue34848`) and a corresponding UI test
to verify that the `Opened` and `Closed` events are raised correctly on
MacCatalyst and other platforms, using platform-specific logic to close
the DatePicker. (`TestCases.HostApp/Issues/Issue34848.cs`,
`TestCases.Shared.Tests/Tests/Issues/Issue34848.cs`)
[[1]](diffhunk://#diff-652f2cd8a1e252cf8db29bf33034066d08ec5e3b43ec76a77d477824a08a1f44R1-R46)
[[2]](diffhunk://#diff-a57ba10bf75c97b2a6f28c075585a1066df8c7c2c31751e3c799177fca6560b4R1-R42)

**General Codebase Improvements:**

* Minor code cleanup and improved organization in the DatePicker handler
for MacCatalyst, including the addition of necessary using directives.
(`DatePickerHandler.MacCatalyst.cs`)


<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes dotnet#34848 

### Tested the behavior in the following platforms

- [ ] Windows
- [ ] Android
- [ ] iOS
- [x] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/73c4686f-30d0-4a57-bb8d-be6b2d4b7cda">
| <video
src="https://github.com/user-attachments/assets/65d6f44e-b145-48de-b41a-77b6abefabc4">
|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
…and an EmptyView is set in iOS, Mac platform (dotnet#34989)

<!-- 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 Details:

On iOS and macCatalyst, the CollectionView Header/Footer is not visible
when ItemsSource is not set (or empty) and an EmptyView is configured.
       
### Root Cause:

iOS/macCatalyst uses UICollectionViewCompositionalLayout, which renders
the Header and Footer as global boundary supplementary items configured
on NSCollectionLayoutConfiguration. The core problem is that
UICollectionViewCompositionalLayout simply does not render these global
boundary items when the collection reports 0 sections. When ItemsSource
is null or empty, the data source returns 0 sections, causing the layout
engine to skip rendering the Header/Footer entirely. Android is
unaffected because its layout engine handles this case differently.

### Description of Change:

The fix overrides NumberOfSections in StructuredItemsViewController2.cs
to return at least 1 section whenever the data source would return 0
sections but a Header or Footer is configured. This tricks the
UICollectionViewCompositionalLayout into having one phantom empty
section, which satisfies the layout engine and causes the Header/Footer
to render correctly. A guard ensures this only applies to non-grouped
collections — grouped collections use per-section supplementary items
and would crash if given a phantom section that has no corresponding
group data in the source.

**Tested the behavior in the following platforms:**

- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  dotnet#34897          

### Screenshots
| Before  | After  |
|---------|--------|
| <img width="300" height="600" alt="Before_34897"
src="https://github.com/user-attachments/assets/9e883564-d5cd-4f89-b238-f5b7c8bf434c"
/> | <img width="300" height="600" alt="After_34897"
src="https://github.com/user-attachments/assets/eb045764-1bee-4f52-b97f-65e39f0cdfc7"
/> |
@dotnet dotnet deleted a comment from MauiBot Apr 17, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 17, 2026
@MauiBot

MauiBot commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator

🚦 Gate — Test Before and After Fix

👋 @Tamilarasan-Paranthaman — new gate results are available. Please review the latest session below.

🚦 Gate Session273a76b · tests updated. · 2026-04-17 19:36 UTC

Gate Result: ❌ FAILED

Platform: WINDOWS · Base: main · Merge base: eb0b82fe

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue27563 Issue27563 ✅ FAIL — 232s ❌ FAIL — 48s
🔴 Without fix — 🖥️ Issue27563: FAIL ✅ · 232s
  Determining projects to restore...
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 42.03 sec).
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 42.11 sec).
  Restored D:\a\1\s\src\Controls\Foldable\src\Controls.Foldable.csproj (in 1.01 sec).
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 1.07 sec).
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 4 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 16 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 190 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 29 ms).
  Restored D:\a\1\s\src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 4.58 sec).
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 35 ms).
  Restored D:\a\1\s\src\Controls\tests\TestCases.HostApp\Controls.TestCases.HostApp.csproj (in 2.48 sec).
  3 of 14 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(11,19): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(17,19): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(26,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnActionSheetRequested(Microsoft.Maui.Controls.Page! sender, Microsoft.Maui.Controls.Internals.ActionSheetArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(35,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnAlertRequested(Microsoft.Maui.Controls.Page! sender, Microsoft.Maui.Controls.Internals.AlertArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(44,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnPromptRequested(Microsoft.Maui.Controls.Page! sender, Microsoft.Maui.Controls.Internals.PromptArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(52,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnPageBusy(Microsoft.Maui.Controls.Page! sender, bool enabled) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(20,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.Subscribe() -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(25,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.Unsubscribe() -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(34,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestAlert(Microsoft.Maui.Controls.Page! page, Microsoft.Maui.Controls.Internals.AlertArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(43,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestActionSheet(Microsoft.Maui.Controls.Page! page, Microsoft.Maui.Controls.Internals.ActionSheetArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(52,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestPrompt(Microsoft.Maui.Controls.Page! page, Microsoft.Maui.Controls.Internals.PromptArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(60,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestPageBusy(Microsoft.Maui.Controls.Page! page, bool isBusy) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]

Build FAILED.

D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(11,19): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(17,19): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(26,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnActionSheetRequested(Microsoft.Maui.Controls.Page! sender, Microsoft.Maui.Controls.Internals.ActionSheetArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(35,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnAlertRequested(Microsoft.Maui.Controls.Page! sender, Microsoft.Maui.Controls.Internals.AlertArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(44,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnPromptRequested(Microsoft.Maui.Controls.Page! sender, Microsoft.Maui.Controls.Internals.PromptArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManagerSubscription.cs(52,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManagerSubscription.OnPageBusy(Microsoft.Maui.Controls.Page! sender, bool enabled) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(20,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.Subscribe() -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(25,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.Unsubscribe() -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(34,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestAlert(Microsoft.Maui.Controls.Page! page, Microsoft.Maui.Controls.Internals.AlertArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(43,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestActionSheet(Microsoft.Maui.Controls.Page! page, Microsoft.Maui.Controls.Internals.ActionSheetArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(52,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestPrompt(Microsoft.Maui.Controls.Page! page, Microsoft.Maui.Controls.Internals.PromptArguments! arguments) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
D:\a\1\s\src\Controls\src\Core\Platform\AlertManager\IAlertManager.cs(60,8): error RS0016: Symbol 'Microsoft.Maui.Controls.Platform.IAlertManager.RequestPageBusy(Microsoft.Maui.Controls.Page! page, bool isBusy) -> void' is not part of the declared public API (https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) [D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
    0 Warning(s)
    12 Error(s)

Time Elapsed 00:02:53.18

🟢 With fix — 🖥️ Issue27563: FAIL ❌ · 48s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13870932
D:\a\1\s\src\Core\src\Platform\Windows\RootNavigationView.cs(285,37): error CS1061: 'MauiToolbar' does not contain a definition for 'HasMenuBarContent' and no accessible extension method 'HasMenuBarContent' accepting a first argument of type 'MauiToolbar' could be found (are you missing a using directive or an assembly reference?) [D:\a\1\s\src\Core\src\Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
C:\Users\VssAdministrator\.nuget\packages\microsoft.windowsappsdk.winui\1.8.251105000\buildTransitive\Microsoft.UI.Xaml.Markup.Compiler.interop.targets(845,9): error MSB3073: The command ""C:\Users\VssAdministrator\.nuget\packages\microsoft.windowsappsdk.winui\1.8.251105000\buildTransitive\..\tools\net6.0\..\net472\XamlCompiler.exe" "D:\a\1\s\artifacts\obj\Core\Debug\net10.0-windows10.0.19041.0\\input.json" "D:\a\1\s\artifacts\obj\Core\Debug\net10.0-windows10.0.19041.0\\output.json"" exited with code 1. [D:\a\1\s\src\Core\src\Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]

Build FAILED.

D:\a\1\s\src\Core\src\Platform\Windows\RootNavigationView.cs(285,37): error CS1061: 'MauiToolbar' does not contain a definition for 'HasMenuBarContent' and no accessible extension method 'HasMenuBarContent' accepting a first argument of type 'MauiToolbar' could be found (are you missing a using directive or an assembly reference?) [D:\a\1\s\src\Core\src\Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
C:\Users\VssAdministrator\.nuget\packages\microsoft.windowsappsdk.winui\1.8.251105000\buildTransitive\Microsoft.UI.Xaml.Markup.Compiler.interop.targets(845,9): error MSB3073: The command ""C:\Users\VssAdministrator\.nuget\packages\microsoft.windowsappsdk.winui\1.8.251105000\buildTransitive\..\tools\net6.0\..\net472\XamlCompiler.exe" "D:\a\1\s\artifacts\obj\Core\Debug\net10.0-windows10.0.19041.0\\input.json" "D:\a\1\s\artifacts\obj\Core\Debug\net10.0-windows10.0.19041.0\\output.json"" exited with code 1. [D:\a\1\s\src\Core\src\Core.csproj::TargetFramework=net10.0-windows10.0.19041.0]
    0 Warning(s)
    2 Error(s)

Time Elapsed 00:00:38.49

⚠️ Issues found
  • Issue27563 FAILED with fix (should pass)
📁 Fix files reverted (183 files)
  • eng/Versions.props
  • src/AI/src/Essentials.AI/PublicAPI/net-ios/PublicAPI.Shipped.txt
  • src/AI/src/Essentials.AI/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/AI/src/Essentials.AI/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt
  • src/AI/src/Essentials.AI/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
  • src/AI/src/Essentials.AI/PublicAPI/net-macos/PublicAPI.Shipped.txt
  • src/AI/src/Essentials.AI/PublicAPI/net-macos/PublicAPI.Unshipped.txt
  • src/BlazorWebView/src/Maui/build/Microsoft.AspNetCore.Components.WebView.Maui.targets
  • src/BlazorWebView/src/SharedSource/WebView2WebViewManager.cs
  • src/BlazorWebView/src/Wpf/BlazorWebView.cs
  • src/Controls/Maps/src/AppHostBuilderExtensions.cs
  • src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xaml
  • src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xaml.cs
  • src/Controls/src/Core/BindableObject.cs
  • src/Controls/src/Core/BindableProperty.cs
  • src/Controls/src/Core/Brush/Brush.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutContentRenderer.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutHeaderContainer.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewSource.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/SlideFlyoutTransition.cs
  • src/Controls/src/Core/FlyoutPage/FlyoutPage.Mapper.cs
  • src/Controls/src/Core/GraphicsView/GraphicsView.cs
  • src/Controls/src/Core/Handlers/Items/Android/Adapters/ReorderableItemsViewAdapter.cs
  • src/Controls/src/Core/Handlers/Items/Android/Adapters/SelectableItemsViewAdapter.cs
  • src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ItemsSourceFactory.cs
  • src/Controls/src/Core/Handlers/Items/Android/ItemsSources/ObservableItemsSource.cs
  • src/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cs
  • src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs
  • src/Controls/src/Core/Handlers/Items/Android/SimpleItemTouchHelperCallback.cs
  • src/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cs
  • src/Controls/src/Core/Handlers/Items/ItemsViewHandler.Windows.cs
  • src/Controls/src/Core/Handlers/Items/SelectableItemsViewHandler.Windows.cs
  • src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs
  • src/Controls/src/Core/Handlers/Items/iOS/CarouselViewController.cs
  • src/Controls/src/Core/Handlers/Items/iOS/MauiCollectionView.cs
  • src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewController.cs
  • src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewDelegator.cs
  • src/Controls/src/Core/Handlers/Items2/CarouselViewHandler2.iOS.cs
  • src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs
  • src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
  • src/Controls/src/Core/Handlers/Items2/iOS/LayoutFactory2.cs
  • src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewController2.cs
  • src/Controls/src/Core/Handlers/Items2/iOS/ReorderableItemsViewDelegator2.cs
  • src/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cs
  • src/Controls/src/Core/Handlers/Shell/ShellItemHandler.Windows.cs
  • src/Controls/src/Core/Handlers/Shell/Windows/ShellView.cs
  • src/Controls/src/Core/Items/MarshalingObservableCollection.cs
  • src/Controls/src/Core/Label/Label.Mapper.cs
  • src/Controls/src/Core/Label/Label.cs
  • src/Controls/src/Core/Label/Label.iOS.cs
  • src/Controls/src/Core/NavigationPage/NavigationPage.cs
  • src/Controls/src/Core/Page/Page.cs
  • src/Controls/src/Core/Platform/AlertManager/AlertManager.cs
  • src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs
  • src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs
  • src/Controls/src/Core/Platform/Windows/CollectionView/ItemContentControl.cs
  • src/Controls/src/Core/Platform/Windows/Extensions/AutoSuggestBoxExtensions.cs
  • src/Controls/src/Core/Platform/iOS/ControlsModalWrapper.cs
  • src/Controls/src/Core/Platform/iOS/Extensions/LabelExtensions.cs
  • src/Controls/src/Core/PlatformConfiguration/WindowsSpecific/FlyoutPage.cs
  • src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt
  • src/Controls/src/Core/RadioButton/RadioButton.cs
  • src/Controls/src/Core/RadioButton/RadioButtonGroup.cs
  • src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs
  • src/Controls/src/Core/Setter.cs
  • src/Controls/src/Core/SetterSpecificity.cs
  • src/Controls/src/Core/Shapes/Shape.cs
  • src/Controls/src/Core/Shell/ShellElementCollection.cs
  • src/Controls/src/Core/Shell/ShellItem.cs
  • src/Controls/src/Core/Style.cs
  • src/Controls/src/Core/TabbedPage/TabbedPage.Windows.cs
  • src/Controls/src/Core/TitleBar/TitleBar.cs
  • src/Controls/src/Core/VisualElement/VisualElement.Platform.cs
  • src/Controls/src/Core/VisualElement/VisualElement.cs
  • src/Controls/src/Core/VisualStateManager.cs
  • src/Controls/src/Core/Window/Window.cs
  • src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
  • src/Core/maps/src/Handlers/Map/MapHandler.Windows.cs
  • src/Core/maps/src/Handlers/MapElement/MapElementHandler.Windows.cs
  • src/Core/maps/src/Handlers/MapPin/MapPinHandler.Android.cs
  • src/Core/maps/src/Handlers/MapPin/MapPinHandler.Windows.cs
  • src/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt
  • src/Core/src/Graphics/ShapeDrawable.cs
  • src/Core/src/Handlers/Button/ButtonHandler.Android.cs
  • src/Core/src/Handlers/Button/ButtonHandler.iOS.cs
  • src/Core/src/Handlers/DatePicker/DatePickerHandler.MacCatalyst.cs
  • src/Core/src/Handlers/DatePicker/DatePickerHandler.cs
  • src/Core/src/Handlers/DatePicker/DatePickerHandler.iOS.cs
  • src/Core/src/Handlers/Editor/EditorHandler.Android.cs
  • src/Core/src/Handlers/Editor/EditorHandler.iOS.cs
  • src/Core/src/Handlers/Entry/EntryHandler.cs
  • src/Core/src/Handlers/Entry/EntryHandler.iOS.cs
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Standard.cs
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Tizen.cs
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
  • src/Core/src/Handlers/Label/LabelHandler.Android.cs
  • src/Core/src/Handlers/Label/LabelHandler.iOS.cs
  • src/Core/src/Handlers/Picker/PickerHandler.Windows.cs
  • src/Core/src/Handlers/Picker/PickerHandler.iOS.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.Android.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.Standard.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.Windows.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.cs
  • src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
  • src/Core/src/Handlers/ShapeView/ShapeViewHandler.Standard.cs
  • src/Core/src/Handlers/ShapeView/ShapeViewHandler.cs
  • src/Core/src/Handlers/ShapeView/ShapeViewHandler.iOS.cs
  • src/Core/src/Handlers/Switch/SwitchHandler.iOS.cs
  • src/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cs
  • src/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cs
  • src/Core/src/Handlers/TimePicker/TimePickerHandler.cs
  • src/Core/src/Handlers/TimePicker/TimePickerHandler.iOS.cs
  • src/Core/src/Handlers/WebView/WebViewHandler.Windows.cs
  • src/Core/src/Handlers/WebView/WebViewHandler.cs
  • src/Core/src/ImageSources/FileImageSourceService/FileImageSourceService.Windows.cs
  • src/Core/src/Layouts/Flex.cs
  • src/Core/src/Platform/Android/ContainerView.cs
  • src/Core/src/Platform/Android/ContentViewGroup.cs
  • src/Core/src/Platform/Android/EditTextExtensions.cs
  • src/Core/src/Platform/Android/LayoutViewGroup.cs
  • src/Core/src/Platform/Android/MauiHybridWebView.cs
  • src/Core/src/Platform/Android/MauiPageControl.cs
  • src/Core/src/Platform/Android/MauiRippleDrawableExtensions.cs
  • src/Core/src/Platform/Android/MauiSearchView.cs
  • src/Core/src/Platform/Android/MauiSwipeView.cs
  • src/Core/src/Platform/Android/MauiWebView.cs
  • src/Core/src/Platform/Android/SafeAreaExtensions.cs
  • src/Core/src/Platform/Android/SemanticExtensions.cs
  • src/Core/src/Platform/Android/TextAlignmentExtensions.cs
  • src/Core/src/Platform/Android/TimePickerExtensions.cs
  • src/Core/src/Platform/Android/WrapperView.cs
  • src/Core/src/Platform/Windows/ContentPanel.cs
  • src/Core/src/Platform/Windows/KeyboardExtensions.cs
  • src/Core/src/Platform/Windows/MauiNavigationView.cs
  • src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
  • src/Core/src/Platform/Windows/RootNavigationView.cs
  • src/Core/src/Platform/Windows/TextBoxExtensions.cs
  • src/Core/src/Platform/Windows/TimePickerExtensions.cs
  • src/Core/src/Platform/Windows/WebViewExtensions.cs
  • src/Core/src/Platform/iOS/ButtonExtensions.cs
  • src/Core/src/Platform/iOS/CollectionViewExtensions.cs
  • src/Core/src/Platform/iOS/DatePickerExtensions.cs
  • src/Core/src/Platform/iOS/LayerExtensions.cs
  • src/Core/src/Platform/iOS/MauiScrollView.cs
  • src/Core/src/Platform/iOS/MauiSearchBar.cs
  • src/Core/src/Platform/iOS/MauiSwipeView.cs
  • src/Core/src/Platform/iOS/MauiTextView.cs
  • src/Core/src/Platform/iOS/MauiView.cs
  • src/Core/src/Platform/iOS/SearchBarExtensions.cs
  • src/Core/src/Platform/iOS/TextViewExtensions.cs
  • src/Core/src/Platform/iOS/TimePickerExtensions.cs
  • src/Core/src/Platform/iOS/WrapperView.cs
  • src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt
  • src/Core/src/PublicAPI/net-ios/PublicAPI.Shipped.txt
  • src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt
  • src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt
  • src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
  • src/Essentials/src/DeviceDisplay/DeviceDisplay.ios.cs
  • src/Essentials/src/MediaPicker/MediaPicker.ios.cs
  • src/Essentials/src/Share/Share.android.cs
  • src/Graphics/src/Graphics/Platforms/Android/GraphicsExtensions.cs
  • src/Graphics/src/Graphics/Platforms/Android/PlatformCanvas.cs
  • src/Graphics/src/Graphics/Platforms/Android/PlatformGraphicsView.cs
  • src/Graphics/src/Graphics/Platforms/MaciOS/PlatformCanvas.cs
  • src/Graphics/src/Graphics/Platforms/Windows/PlatformGraphicsView.cs
  • src/Graphics/src/Graphics/Platforms/Windows/PlatformImage.cs
  • src/Graphics/src/Graphics/Platforms/iOS/PlatformGraphicsView.cs
  • src/Graphics/src/Graphics/Platforms/iOS/UIImageExtensions.cs
  • src/Templates/src/templates/maui-mobile/PageModels/MainPageModel.cs

New files (not reverted):

  • src/Controls/src/Core/Handlers/Items/iOS/ReorderableItemsViewExtensions.cs
  • src/Controls/src/Core/Platform/AlertManager/IAlertManager.cs
  • src/Controls/src/Core/Platform/AlertManager/IAlertManagerSubscription.cs
  • src/Core/src/Handlers/HybridWebView/HybridWebViewHelper.cs
  • src/Core/src/Platform/Android/Resources/values/strings.xml

@Vignesh-SF3580

Copy link
Copy Markdown
Contributor

Could you please review comments?

@kubaflo I have already addressed all the AI concerns in this PR, and no new concerns have been raised. There were conflicts in this PR, which have now been resolved. Regarding the gate failures, there is a known issue on Windows when using CarouselView with Loop = true, so the test has been restricted on Windows.

@MauiBot

MauiBot commented Apr 17, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI Summary

👋 @Tamilarasan-Paranthaman — new AI review results are available. Please review the latest session below.

📊 Review Session273a76b · tests updated. · 2026-04-18 00:44 UTC
🔍 Pre-Flight — Context & Validation

Issue: #27563 - [Windows] Indicator view causes looping with CarouselView
PR: #27880 - [Windows] Fix Indicator View Causing Looping Issue with CarouselView
Platforms Affected: Windows (fix is Windows-only in CarouselViewHandler.Windows.cs)
Files Changed: 1 implementation, 2 test (+ 2 snapshot PNG files)

Key Findings

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: high
Errors: 1 | Warnings: 2 | Suggestions: 1

Key code review findings:

  • ❌ Windows CI has failing required checks: maui-pr (Build .NET MAUI Build Windows Debug/Release/Pack) — must be resolved before merge
  • ⚠️ UpdateInitialPosition Loop centering removal (CenterMode = true; ScrollIntoView(item)) carries an acknowledged regression risk for Loop mode on Windows, with no test coverage to validate it (CarouselViewHandler.Windows.cs:388-401)
  • ⚠️ Zero Windows test coverage: both new tests use #if !WINDOWS guards — Issue27563.cs:15 and Issue27563.cs:35
  • 💡 _lastScrolledToPosition reset in CarouselScrolled could stay stale at intermediate positions during animated scroll (CarouselViewHandler.Windows.cs:549-553)

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #27880 Introduce _lastScrolledToPosition guard field; add UpdatePosition ScrollTo path; remove Loop CenterMode block from UpdateInitialPosition; reset tracker on user scroll and collection change ❌ GATE FAILED CarouselViewHandler.Windows.cs, Issue27563.cs, Issue27563.cs (HostApp) Original PR — Gate failed

🔬 Code Review — Deep Analysis

Code Review — PR #27880

Independent Assessment

What this changes: Introduces a _lastScrolledToPosition field (sentinel = -1) to prevent re-entrant ScrollTo calls in the Windows CarouselViewHandler. UpdateCurrentItem now guards against issuing a ScrollTo when it has already been issued for the same index. UpdatePosition gains a new ScrollTo path (guarded by IsDragging/IsScrolling) so IndicatorView-driven position changes actually animate the carousel. CarouselScrolled resets the tracker when the visible position diverges from the last-programmatic position. The Loop-specific CenterMode + ScrollIntoView block in UpdateInitialPosition is removed entirely.

Inferred motivation: When an IndicatorView triggers a Position change, UpdatePosition fires and calls ScrollTo. That scroll fires CarouselScrolled, which updates Position, which fires UpdatePosition again — an infinite feedback loop. The _lastScrolledToPosition field breaks this cycle.


Reconciliation with PR Narrative

Author claims: Root cause is a re-entrant scroll loop triggered by IndicatorView → UpdatePositionCarouselScrolledUpdatePosition. The fix is the _lastScrolledToPosition guard, plus a reset on collection changes and user-initiated scrolls. The Loop centering block is declared redundant, with the PR description itself flagging a potential regression. Tests are Windows-disabled due to #29245.

Agreement: The root cause analysis matches the code. The re-entrancy mechanism is real and the guard design is sound conceptually.

Disagreement: The PR's own description says "(see code review note about potential regression)" about the Loop centering removal — meaning the author is aware this may break Loop mode initialization on Windows but has not tested or guarded it. The CI confirms Windows builds are failing.


Findings

❌ Error — Windows CI is failing (required checks)

The PR has failing required checks:

  • maui-pr (Build .NET MAUI Build Windows (Debug)) — ❌ FAIL
  • maui-pr (Build .NET MAUI Build Windows (Release)) — ❌ FAIL
  • maui-pr (Pack .NET MAUI Pack Windows) — ❌ FAIL

These failures must be investigated and resolved before merge. A Windows-targeted fix with failing Windows builds cannot be considered ready.

⚠️ Warning — Loop centering removal may regress Loop mode initial scroll

UpdateInitialPosition previously ran:

// pre-PR
if (Element.Loop)
{
    var item = ItemsView.CurrentItem ?? ListViewBase.Items.FirstOrDefault();
    _loopableCollectionView.CenterMode = true;
    ListViewBase.ScrollIntoView(item);
    _loopableCollectionView.CenterMode = false;
}

This is replaced by nothing. The PR assumes that UpdateCurrentItem() / UpdatePosition() — which now call ItemsView.ScrollTo(...) — are equivalent. But the original code directly sets _loopableCollectionView.CenterMode = true before calling ListViewBase.ScrollIntoView. The MAUI cross-platform ScrollTo path goes through base.ScrollTo() and does not set CenterMode. If LoopableCollectionView.CenterMode is required for Loop to scroll to the virtual center of the duplicated item list (enabling bidirectional swipe), removing this will break Loop mode initial positioning on Windows. The author acknowledges this risk inline but does not address it.

Adjacent risk: CarouselView is among the most-regressed components in MAUI. Loop mode, ScrollTo, and CurrentItem are all in its highest-risk surface. A fix for one scenario that potentially regresses Loop initialization is a pattern seen in prior reverts.

⚠️ Warning — Zero automated test coverage for the Windows fix

All new tests are guarded:

#if !WINDOWS  // VerifyCarouselViewIndicatorPositionWithoutLooping
#if !WINDOWS && !MACCATALYST  // VerifyCarouselViewScrolling

The PR targets a Windows-only code path (CarouselViewHandler.Windows.cs). There is no test that runs on Windows and verifies the fix. If CI for Windows is ever unblocked (#29245), there will still be no regression coverage for this PR's change.

Separately, reviewer jsuarezruiz has an unresolved open thread requesting:

"Can extend the new test to cover loop initial centering and the reentrancy case (tap indicator repeatedly while swiping), and a test that toggles Position to the same value to ensure no scroll storm occurs."

This is still open.

💡 Suggestion — _lastScrolledToPosition can stay stale after a programmatic scroll completes during an animated transition

In CarouselScrolled, the reset logic is:

if (position != _lastScrolledToPosition)
{
    _lastScrolledToPosition = -1;
}

This means: if CarouselScrolled fires at intermediate positions during an animated scroll (position values in between source and target), and one of those intermediate positions equals _lastScrolledToPosition, the guard is not cleared. This is probably harmless in practice because UpdatePosition only fires on Position property changes (not on every scroll frame), but it's worth a comment clarifying the intended semantics.


Devil's Advocate

Challenging the Loop centering concern: Maybe LoopableCollectionView.CenterMode is only ever needed for the old ScrollIntoView path and the new ItemsView.ScrollTo(position: ScrollToPosition.Center) achieves the same effect. Without reading LoopableCollectionView's implementation, this cannot be fully ruled out. But the PR author themselves flagged a potential regression, and all Windows tests are disabled, so there is no empirical confirmation either way.

Challenging the CI failure concern: The inflight/current branch is a rolling integration branch, and the Windows build failures could be pre-existing baseline failures unrelated to this PR. If that's the case, flagging them as blockers may be unfair. However, CI failures must be investigated before merge — it's the author's responsibility to demonstrate these failures are not caused by this PR.

Challenging the test gap concern: The tests pass on Android and iOS, and the fix only touches CarouselViewHandler.Windows.cs. If the logic is correct, it's correct, and the Windows Appium limitation is a separate infra problem. That said, the Loop centering removal is specifically Windows code, so no coverage there is a real gap.


Blast Radius

The _lastScrolledToPosition guard is local state on the Windows handler instance — it doesn't affect other platforms. The loop in UpdatePositionCarouselScrolled runs for ALL Windows users of CarouselView with IndicatorView. The removal of the Loop centering block affects ALL Windows users of CarouselView with Loop = true.


Failure-Mode Probes

  1. Loop = true, app launches, first item is not at center: Without CenterMode = true in UpdateInitialPosition, can users swipe in both directions? If LoopableCollectionView requires centering to enable wrap-around behavior, Loop mode breaks on first load.

  2. User sets IndicatorView.Position to current carousel position: _lastScrolledToPosition will equal the position → UpdatePosition skips ScrollTo → OK.

  3. User sets IndicatorView.Position to the same value twice rapidly: First call sets _lastScrolledToPosition and fires ScrollTo. Second call (same value) is blocked. CarouselScrolled fires when scroll completes and resets tracker if position != _lastScrolledToPosition — but since they match, tracker is NOT reset. If position is set again later to the same value, it will still be blocked. Could cause "stuck" navigation after a same-index scroll.

  4. Collection source changes mid-scroll: OnCollectionItemsSourceChanged resets _lastScrolledToPosition = -1 correctly, unblocking future navigation.


Verdict: NEEDS_CHANGES

Confidence: High
Summary: The re-entrancy guard design is conceptually correct and the root cause diagnosis matches the code. However, the PR has failing required Windows CI checks, the Loop initial centering removal carries an acknowledged (but unaddressed) regression risk with no Windows test coverage to validate it, and an open reviewer request for broader test scenarios remains unresolved. These items need to be addressed — specifically: confirm or fix the Windows CI failures, verify Loop mode initialization still works on Windows after the CenterMode removal (or restore the block in a way that doesn't reintroduce the loop), and respond to the open test-coverage thread.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Boolean guard flag _isScrollingProgrammatically prevents re-entrant ScrollTo in UpdateCurrentItem/UpdatePosition; resets in OnScrollViewChanged(!IsIntermediate); preserves Loop CenterMode block PASS (33/34; 1 pre-existing screenshot flake) 1 file No same-index stuck failure mode; simpler than PR position-tracking
2 try-fix (claude-sonnet-4.6) Event unsubscription counter _pendingProgrammaticScrolls FAIL (WinUI collection-change ViewChanged side effects cause premature resubscription) 2 files Fundamentally sound for primary loop but needs position-state for collection-change edge cases
3 try-fix (gpt-5.3-codex) Scoped counter at binding layer to detect scroll-originated Position changes BLOCKED (RS0016 PublicAPI build errors in unrelated files) 1 file Not empirically validated
4 try-fix (gpt-5.4, sub for gemini) SetValueFromRenderer for handler-originated Position/CurrentItem sync BLOCKED (build errors in unrelated files) 1 file Not empirically validated
5 try-fix (claude-opus-4.6 cross-pollination) Extend existing _gotoPosition guard to non-animated programmatic scrolls FAIL (35/37; timing gap after Task completes) 1 file _gotoPosition lifecycle tied to async Task, cannot bridge post-Task event delivery
6 try-fix (claude-sonnet-4.6 cross-pollination) Target-aware suppression: _programmaticScrollInProgress + _programmaticScrollTarget; reset only when CarouselScrolled confirms target position FAIL (36/37; also patched unrelated files) 3 files (2 unrelated) More robust than PR; 1 remaining failure (Issue29529) is pre-existing
PR PR #27880 _lastScrolledToPosition position-tracking field; removes Loop CenterMode block from UpdateInitialPosition GATE FAILED 3 files Windows tests excluded via #if !WINDOWS; Loop centering removal unvalidated

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes Extend _gotoPosition to non-animated scrolls -> Attempt 5 (FAIL)
claude-sonnet-4.6 2 Yes Target-aware suppression with position-confirmed reset -> Attempt 6 (FAIL)
gpt-5.3-codex 2 Yes Deferred/coalesced sync state machine (_pendingTargetPosition + _syncOrigin) - not run (round 3 limit)
claude-opus-4.6 3 NO NEW IDEAS -
claude-sonnet-4.6 3 NO NEW IDEAS -

Exhausted: Yes (6 attempts, 3 cross-pollination rounds, all 4 models queried)
Selected Fix: Candidate #1 (boolean _isScrollingProgrammatically flag) - only independently validated passing solution. Simpler than PR approach, avoids same-index stuck failure mode, preserves Loop CenterMode block in UpdateInitialPosition.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #27563 (Windows CarouselView + IndicatorView infinite scroll loop); 1 impl file, 2 test files
Gate ❌ FAILED Windows — tests did not behave as expected
Try-Fix ✅ COMPLETE 6 attempts, 1 passing (Candidate #1 boolean flag); agent found a better alternative
Report ✅ COMPLETE

Summary

PR #27880 fixes a real, verified Windows-only bug: an infinite scroll loop when IndicatorView drives CarouselView position changes. The root cause diagnosis is correct and the _lastScrolledToPosition guard design is conceptually sound. However, the PR has multiple blocking issues:

  1. Gate failed — Windows CI required checks are failing
  2. Loop regression risk — the PR removes the CenterMode + ScrollIntoView block from UpdateInitialPosition for Loop mode without validating that Loop initialization still works on Windows
  3. Zero Windows test coverage — all new tests are #if !WINDOWS guarded, despite this being a Windows-only fix
  4. Open reviewer threadjsuarezruiz has an unresolved request to extend tests to cover loop centering, reentrancy while swiping, and scroll storm prevention

A simpler, independently validated alternative (Candidate #1) was found during Try-Fix exploration.

Root Cause

On Windows, when IndicatorView.Position is set externally, CarouselView.UpdatePosition fires and calls ItemsView.ScrollTo(...). This triggers CarouselScrolled, which calls SetCarouselViewPosition, which updates CarouselView.Position, which fires UpdatePosition again — creating an infinite feedback loop. The original code did not call ScrollTo inside UpdatePosition, so the bug was introduced when IndicatorView support was added without a re-entrancy guard.

Fix Quality

PR's fix (_lastScrolledToPosition):

  • ✅ Correct conceptual approach — tracks the last position sent to ScrollTo to suppress re-entrant calls
  • ❌ "Same-index stuck" failure mode: after a programmatic scroll completes, CarouselScrolled fires with position == _lastScrolledToPosition, so the tracker is NOT reset. A subsequent call to the same index is silently dropped
  • ❌ Removes Loop CenterMode block with an acknowledged regression risk and no compensating test
  • ❌ All Windows tests excluded — no empirical validation on the target platform

Candidate #1 (boolean _isScrollingProgrammatically flag):

  • ✅ Independently validated — 33/34 existing CarouselView tests pass (1 pre-existing screenshot flake, unrelated)
  • ✅ Simpler — 1 field vs. PR's position-tracking approach with reset logic in 4 locations
  • ✅ No same-index stuck failure mode — resets on OnScrollViewChanged(!IsIntermediate) regardless of position value
  • ✅ Preserves Loop CenterMode block in UpdateInitialPosition — no regression risk for Loop=true users
  • ⚠️ Like the PR, does not add Windows test coverage — the #if !WINDOWS guard on tests remains a gap

Recommendation

Suggest replacing the PR's _lastScrolledToPosition approach with a boolean guard flag (_isScrollingProgrammatically) that resets on scroll completion rather than tracking position values. This:

  1. Fixes the infinite loop bug
  2. Preserves Loop mode initial centering behavior
  3. Avoids the same-index stuck failure mode in the PR's tracker
  4. Is empirically validated against existing CarouselView tests

Additionally, the PR should:

  • Investigate and resolve Windows CI build failures before merge
  • Add at minimum one Windows test that verifies the IndicatorView loop is fixed (even if it requires resolving issue [Testing] Appium test does not perform the tap action with CarouselView. #29245 first, or designing a test that doesn't depend on Appium swipe)
  • Address the open reviewer thread requesting loop centering and reentrancy test coverage

Fix Comparison

Aspect PR's _lastScrolledToPosition Candidate #1 Boolean Flag
Test result ❌ GATE FAILED ✅ PASS (33/34)
Fields added 1 int 1 bool
Reset locations 3 (CarouselScrolled, OnCollectionItemsSourceChanged, reset to -1 in various) 1 (OnScrollViewChanged)
Same-index stuck? Yes — tracker not reset when target == last No — resets on any scroll end
Loop CenterMode Removed (regression risk) Preserved
Windows tests None (all #if !WINDOWS) None (same gap)

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you please review the AI's summary?

@PureWeen PureWeen force-pushed the inflight/current branch 2 times, most recently from 447e0f6 to f3ea859 Compare April 28, 2026 10:29
@github-actions github-actions Bot force-pushed the inflight/current branch from 7492574 to d2f1f64 Compare May 6, 2026 09:20
@Vignesh-SF3580

Copy link
Copy Markdown
Contributor

The reported looping issue is resolved in the latest MAUI version 10.0.60 and is fixed by the changes in PR #24867. Therefore, closing this PR.

@github-actions github-actions Bot locked and limited conversation to collaborators Jun 6, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-collectionview CollectionView, CarouselView, IndicatorView community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) stale Indicates a stale issue/pr and will be closed soon

Projects

None yet