Skip to content

[iOS/Mac] Fix CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2 - #36287

Merged
kubaflo merged 3 commits into
dotnet:inflight/currentfrom
Dhivya-SF4094:fix/indicator-position-update
Jul 15, 2026
Merged

[iOS/Mac] Fix CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2#36287
kubaflo merged 3 commits into
dotnet:inflight/currentfrom
Dhivya-SF4094:fix/indicator-position-update

Conversation

@Dhivya-SF4094

Copy link
Copy Markdown
Contributor

Issue Details:

The position and the IndicatorView don't update to reflect the current position in the CarouselView.

Root Cause

  • The issue occurs only in a two-page navigation flow, where CarouselView.Position is updated while the CarouselView handler is detached (for example, after navigating to another page).

  • During this time, the managed Position property is updated through data binding, but CurrentItem remains unchanged because it is only synchronized while the handler is attached. When the page is navigated back to, the existing reattachment logic always restored the carousel based on CurrentItem, effectively treating it as the source of truth. As a result, any Position changes made while detached were ignored, leaving the CarouselView, IndicatorView, and bound Position state out of sync.

  • A second issue was observed on iOS 26, where a pending _scrollDebounce callback could still execute after the controller was torn down. This stale callback could invoke ScrollToPosition using outdated state and overwrite the internally tracked position, causing the carousel position to drift across navigation cycles.

Description of Change

  • Introduced _lastSyncedPosition to track the last position that was successfully synchronized between the controller and the CarouselView.
  • During reattachment, the handler now determines whether Position changed while it was detached by comparing the current Position with _lastSyncedPosition.
  • If Position changed while detached, it is treated as the source of truth and the carousel is restored to the updated position.
  • Otherwise, the existing CurrentItem-based restoration is preserved, which correctly handles scenarios such as collection changes while the page was inactive.
  • This ensures that explicit Position updates made through bindings or commands while the handler is detached are honored when the page is shown again, while preserving the previous behavior for collection mutation scenarios.
  • Resolved a secondary synchronization issue by cancelling and disposing the pending _scrollDebounce operation during TearDown, preventing delayed scroll callbacks from executing after the controller has been detached.
  • Additionally, a guard was added to ignore debounce callbacks when the resolved CurrentItem position is invalid, preventing stale callbacks from invoking ScrollToPosition and overwriting the tracked synchronized position. This eliminates the position drift observed on repeated navigation cycles on iOS 26.

Validated the behaviour in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Issues Fixed:

Fixes #29312

Screenshots

Before  After 
29312_beforeFix.mov
29312_AfterFix.mov

@github-actions

github-actions Bot commented Jul 2, 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 -- 36287

Or

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

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jul 2, 2026
@github-actions github-actions Bot added area-controls-collectionview CollectionView, CarouselView, IndicatorView platform/ios labels Jul 2, 2026
@Dhivya-SF4094 Dhivya-SF4094 changed the title [WIP] [iOS/Mac] Fix CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2 [iOS/Mac] Fix CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2 Jul 2, 2026
@sheiksyedm
sheiksyedm marked this pull request as ready for review July 3, 2026 10:46
Copilot AI review requested due to automatic review settings July 3, 2026 10:46

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.

Pull request overview

This PR fixes an iOS/MacCatalyst CarouselView2 (Items2) synchronization bug where CarouselView.Position (and thus IndicatorView.Position) could become stale after navigation detach/reattach, and adds iOS 26-specific cleanup to prevent a delayed debounce callback from applying stale state after teardown.

Changes:

  • Track and compare a new _lastSyncedPosition to detect Position changes that occurred while the handler was detached, and restore based on Position vs CurrentItem accordingly.
  • Cancel/dispose the iOS 26 _scrollDebounce operation during teardown and guard against invalid CurrentItem index in delayed callbacks.
  • Re-enable the VerifyIndicatorView_Position feature-matrix UI test on iOS/MacCatalyst by narrowing the failing-platform preprocessor condition to Windows only.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs Adds detach/reattach position reconciliation and cancels pending iOS 26 debounce callbacks during teardown.
src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs Enables the IndicatorView position UI test on iOS/MacCatalyst (keeps it excluded for Windows).

Comment on lines 138 to 149
if (InitialPositionSet && ItemsView is CarouselView carousel)
{
carousel.SetValueFromRenderer(CarouselView.CurrentItemProperty, null);
carousel.SetValueFromRenderer(CarouselView.PositionProperty, 0);
}

// A new items source means a fresh baseline. Reset the tracked position so the next
// re-attach treats the source swap as a reset (Position 0) rather than a detached
// position change. This keeps a new ItemsSource/ViewModel resetting to the first item,
// matching Android behavior.
_lastSyncedPosition = 0;
_isUpdating = false;
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 3, 2026
@MauiBot MauiBot added s/agent-gate-failed AI could not verify tests catch the bug 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) labels Jul 3, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 3, 2026

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could you please add snapshots?

@kubaflo

kubaflo commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

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

@kubaflo

This comment has been minimized.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Tests Failure Analysis

@Dhivya-SF4094 — test-failure review results are available based on commit f6816cc.
To request a fresh review after new comments, commits, or CI runs, comment /review tests.

Overall Not ready Failures 12 Baseline 0 on base Platform iOS/Mac

Test Failure Review: Not ready - click to expand

Overall verdict: Not ready

Two macOS DragAndDrop tests (DragAndDropWithAnElementThatIsRemoved, AcceptedOperationNoneDisablesDropOperation) are deterministically regressed vs the base branch and must be resolved before merge. An additional 10 failures (Windows device-test timeouts, recurring WebView flakiness, and build-publish errors) could not be attributed deterministically, and 7 device-test checks were green but could not be positively confirmed clean.

Coverage: 167 checks · 159 passing/neutral/skipped · 8 failing · 0 pending · 0 inaccessible · 1 unmapped · 11 unexplained build legs · 0 unaccounted failing checks · 0 aborted failing checks · 0 canceled-build checks · 7 device-test unverified · 10 unattributed · 2 regressed-vs-base. Deterministic ceiling: Not ready — 2 regressions vs base (DragAndDrop macOS), 10 unattributed failures, 11 unexplained build legs, 7 unverified device-test checks, 1 unmapped failing check (Build Analysis).

Failure Verdict On base? Evidence
DragAndDropWithAnElementThatIsRemoved Likely PR-caused regressed deterministicAttribution=regressed-vs-base; green on base build 1491320, red on this PR; macOS UITests DragAndDrop, System.TimeoutException timed out waiting for element; 2 occurrences; build 1493733 job f78caf54
AcceptedOperationNoneDisablesDropOperation Likely PR-caused regressed deterministicAttribution=regressed-vs-base; green on base build 1491320, red on this PR; macOS UITests DragAndDrop, System.TimeoutException timed out waiting for element; 2 occurrences; build 1493733 job f78caf54
DeviceTestsWindows (Windows) - build error Needs human investigation also-red indeterminate; leg also red on base; PowerShell exited with code 1; build 1493734
device-test work item incomplete (Controls.DeviceTests-unpackaged) Needs human investigation no HybridWebView timeout after 480 s, crash dump present; 713 passed / 0 failed of tests that ran; incomplete run may mask PR-caused failures in tests that never ran; Helix job 7a871f06
device-test work item incomplete (Controls.DeviceTests-packaged) Needs human investigation no HybridWebView timeout after 480 s, crash dump present; 713 passed / 0 failed of tests that ran; incomplete run may mask PR-caused failures in tests that never ran; Helix job 7a871f06
CookiesCorrectlyLoadWithMultipleWebViews Needs human investigation no indeterminate; ci-scan match #36117 (recurring); System.TimeoutException; 6 occurrences across runs; absent on base; no exact base match to dismiss; build 1493733
WebViewNoCrashPopup Needs human investigation no indeterminate; ci-scan match #36037 (recurring); StaleElementReferenceException; 2 occurrences; absent on base; no exact base match to dismiss; build 1493733
WebViewDoesntCrashWhenLoadingAHeavyPageAndUsingExecutionModeSeparateProcess Needs human investigation no indeterminate; ci-scan match #36245 (recurring); app became unresponsive, force-terminated; 4 occurrences; absent on base; no exact base match to dismiss; build 1493733
Publish the mac_ui_tests_controls test results - build error (DragAndDrop leg) Needs human investigation no indirect test-publish error; test failures detected in result files; absent on base; build 1493733 job f78caf54
Publish the android_ui_tests_controls_30 test results - build error Needs human investigation no indirect test-publish error; test failures detected in result files; inconclusive on base; build 1493733 job 302f9872
Publish the ios_ui_tests_mono_controls_latest test results - build error Needs human investigation no indirect test-publish error; test failures detected in result files; absent on base; build 1493733 job 66b9b155
Publish the mac_ui_tests_controls test results - build error (WebView leg) Needs human investigation no indirect test-publish error; test failures detected in result files; absent on base; build 1493733 job 84120f13

Recommended action

Investigate the two macOS DragAndDrop regressions (DragAndDropWithAnElementThatIsRemoved, AcceptedOperationNoneDisablesDropOperation) — these are deterministically green on base and red on this PR and must be resolved before merge. The WebView and Windows device-test failures appear recurring or infrastructure-related but require human confirmation before dismissal.

Evidence details

PR builds:

  • maui-pr-uitests: Build 1493733 — 27 failed timeline records, 20 distinct log/test failures
  • maui-pr-devicetests: Build 1493734 — 4 failed timeline records, 3 distinct failures

Base builds used for comparison:

  • maui-pr-uitests baseline: Build 1491320 on refs/heads/main (14 baseline failures; only first 8 of 12 failed logs were inspected — baseline list may be incomplete)
  • maui-pr-devicetests baseline: Build 1491321 on refs/heads/main (1 baseline failure)

DragAndDrop regressions (macOS): Both DragAndDropWithAnElementThatIsRemoved and AcceptedOperationNoneDisablesDropOperation produce System.TimeoutException: Timed out waiting for element in the macOS UITests DragAndDrop leg (job f78caf54). Both pass on base build 1491320. While the PR targets iOS/Mac CarouselView handler code, the macOS changes may have an unintended interaction with drag-and-drop element tracking.

Windows device tests: Helix job 7a871f06 timed out waiting for HybridWebView results (480 s) in both packaged and unpackaged work items; crash dumps were present. 713 tests ran successfully before the timeout. The Windows build error leg (DeviceTestsWindows (Windows) - build error) is also red on the base branch, suggesting a pre-existing infrastructure issue.

WebView test failures: CookiesCorrectlyLoadWithMultipleWebViews (6 occurrences), WebViewNoCrashPopup (2 occurrences), and WebViewDoesntCrashWhenLoadingAHeavyPageAndUsingExecutionModeSeparateProcess (4 occurrences) have ci-scan matches against known recurring issues (#36117, #36037, #36245). However, without an exact match on the base build, they cannot be deterministically dismissed as pre-existing.

11 unexplained build legs: Legs including "Controls CarouselView", "Controls Page,Performance,Picker,ProgressBar", "Install .NET", "Controls (vlatest) WebView", "Controls (API 30) CollectionView", and others produced no extractable failure (build breaks with no test name, or unreadable logs). These logs must be reviewed before trusting any verdict.

7 unverified device-test green checks: Android (CoreCLR/Mono), iOS (Mono), MacCatalyst (Mono), and Windows build device-test checks appear green, but XHarness exits 0 even on failure and no Helix all-clean confirmation or authenticated test-API read was possible. A human should confirm these checks are genuinely clean.

Build Analysis check: Unmapped — no inspectable AzDO build evidence. Review its details page directly.

PR scope: 2 files changed, 1 test file. Platform labels: platform/ios. Area labels: area-controls-collectionview. Changed areas: CarouselView handler (iOS/Mac). Base branch: main.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 14, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@Dhivya-SF4094 — new AI review results are available based on this last commit: f6816cc. To request a fresh review after new comments or commits, comment /review rerun.

Gate Failed Confidence Low Platform iOS


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ❌ FAILED

Platform: IOS · Base: main · Merge base: 0395a53b

🩺 Fix does not pass the tests — every test still fails after applying the fix. The PR's change does not resolve the failure(s).

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ IndicatorViewFeatureTests IndicatorViewFeatureTests ✅ FAIL — 409s ❌ FAIL — 776s
🔴 Without fix — 🖥️ IndicatorViewFeatureTests: FAIL ✅ · 409s

(truncated to last 15,000 chars)

age:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifySelectedIndicatorColorWithIndicatorSize.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifySelectedIndicatorColorWithIndicatorSize() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 391
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:04:37 PM VerifySelectedIndicatorColorWithFlowDirection Start
>>>>> 7/14/2026 4:04:44 PM VerifySelectedIndicatorColorWithFlowDirection Stop
>>>>> 7/14/2026 4:04:44 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifySelectedIndicatorColorWithFlowDirection [7 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifySelectedIndicatorColorWithFlowDirection.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifySelectedIndicatorColorWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 406
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:04:45 PM VerifyIndicatorColorWithFlowDirection Start
>>>>> 7/14/2026 4:04:52 PM VerifyIndicatorColorWithFlowDirection Stop
>>>>> 7/14/2026 4:04:52 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorColorWithFlowDirection [7 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorColorWithFlowDirection.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorColorWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 421
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:04:53 PM VerifyIndicatorHideSingleWithIndicatorSize Start
>>>>> 7/14/2026 4:05:03 PM VerifyIndicatorHideSingleWithIndicatorSize Stop
>>>>> 7/14/2026 4:05:03 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorHideSingleWithIndicatorSize [10 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorHideSingleWithIndicatorSize.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorHideSingleWithIndicatorSize() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 440
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:05:04 PM VerifyIndicatorSizeWithFlowDirection Start
>>>>> 7/14/2026 4:05:11 PM VerifyIndicatorSizeWithFlowDirection Stop
>>>>> 7/14/2026 4:05:11 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorSizeWithFlowDirection [7 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorSizeWithFlowDirection.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorSizeWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 470
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:05:12 PM VerifyIndicatorSizeWithMaximumVisible Start
>>>>> 7/14/2026 4:05:19 PM VerifyIndicatorSizeWithMaximumVisible Stop
>>>>> 7/14/2026 4:05:19 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorSizeWithMaximumVisible [8 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorSizeWithMaximumVisible.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorSizeWithMaximumVisible() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 483
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:05:20 PM VerifySelectedIndicatorColorWithIndicatorColor Start
>>>>> 7/14/2026 4:05:28 PM VerifySelectedIndicatorColorWithIndicatorColor Stop
>>>>> 7/14/2026 4:05:28 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifySelectedIndicatorColorWithIndicatorColor [8 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifySelectedIndicatorColorWithIndicatorColor.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifySelectedIndicatorColorWithIndicatorColor() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 498
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:05:28 PM VerifyIndicatorHideSingleIsFalse Start
>>>>> 7/14/2026 4:05:38 PM VerifyIndicatorHideSingleIsFalse Stop
>>>>> 7/14/2026 4:05:38 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorHideSingleIsFalse [10 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorHideSingleIsFalse.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorHideSingleIsFalse() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 516
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:05:38 PM VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor Start
>>>>> 7/14/2026 4:05:49 PM VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor Stop
>>>>> 7/14/2026 4:05:49 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor [11 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 536
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/IndicatorViewFeatureTests.trx

Test Run Failed.
Total tests: 20
     Passed: 1
     Failed: 19
 Total time: 3.8069 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/IndicatorViewFeatureTests.trx

🟢 With fix — 🖥️ IndicatorViewFeatureTests: FAIL ❌ · 776s

(truncated to last 15,000 chars)

Stack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:00 PM VerifySelectedIndicatorColorWithFlowDirection Start
>>>>> 7/14/2026 4:09:07 PM VerifySelectedIndicatorColorWithFlowDirection Stop
>>>>> 7/14/2026 4:09:07 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifySelectedIndicatorColorWithFlowDirection [7 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifySelectedIndicatorColorWithFlowDirection.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifySelectedIndicatorColorWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 406
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:08 PM VerifyIndicatorColorWithFlowDirection Start
>>>>> 7/14/2026 4:09:15 PM VerifyIndicatorColorWithFlowDirection Stop
>>>>> 7/14/2026 4:09:15 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorColorWithFlowDirection [7 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorColorWithFlowDirection.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorColorWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 421
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:16 PM VerifyIndicatorHideSingleWithIndicatorSize Start
>>>>> 7/14/2026 4:09:26 PM VerifyIndicatorHideSingleWithIndicatorSize Stop
>>>>> 7/14/2026 4:09:26 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorHideSingleWithIndicatorSize [10 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorHideSingleWithIndicatorSize.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorHideSingleWithIndicatorSize() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 440
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:26 PM VerifyIndicatorSizeWithFlowDirection Start
>>>>> 7/14/2026 4:09:34 PM VerifyIndicatorSizeWithFlowDirection Stop
>>>>> 7/14/2026 4:09:34 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorSizeWithFlowDirection [7 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorSizeWithFlowDirection.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorSizeWithFlowDirection() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 470
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:34 PM VerifyIndicatorSizeWithMaximumVisible Start
>>>>> 7/14/2026 4:09:42 PM VerifyIndicatorSizeWithMaximumVisible Stop
>>>>> 7/14/2026 4:09:42 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorSizeWithMaximumVisible [8 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorSizeWithMaximumVisible.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorSizeWithMaximumVisible() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 483
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:42 PM VerifySelectedIndicatorColorWithIndicatorColor Start
>>>>> 7/14/2026 4:09:50 PM VerifySelectedIndicatorColorWithIndicatorColor Stop
>>>>> 7/14/2026 4:09:50 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifySelectedIndicatorColorWithIndicatorColor [8 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifySelectedIndicatorColorWithIndicatorColor.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifySelectedIndicatorColorWithIndicatorColor() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 498
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:09:51 PM VerifyIndicatorHideSingleIsFalse Start
>>>>> 7/14/2026 4:10:00 PM VerifyIndicatorHideSingleIsFalse Stop
>>>>> 7/14/2026 4:10:00 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorHideSingleIsFalse [10 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorHideSingleIsFalse.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorHideSingleIsFalse() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 516
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 7/14/2026 4:10:01 PM VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor Start
>>>>> 7/14/2026 4:10:12 PM VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor Stop
>>>>> 7/14/2026 4:10:12 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor [11 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor.png (2.03% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow

  Stack Trace:
     at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
   at Microsoft.Maui.TestCases.Tests.IndicatorViewFeatureTests.VerifyIndicatorHideSingleIsFalseWithSelectedIndicatorColor() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:line 536
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

NUnit Adapter 4.5.0.0: Test execution complete
Results File: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/IndicatorViewFeatureTests.trx

Test Run Failed.
Total tests: 20
     Passed: 2
     Failed: 18
 Total time: 2.9804 Minutes
>>> TRX_RESULT_FILE: /Users/cloudtest/vss/_work/1/s/CustomAgentLogsTmp/UITests/TestResults/IndicatorViewFeatureTests.trx

⚠️ Failure Details

  • IndicatorViewFeatureTests FAILED with fix (should pass)
    • Device tests: 18 of 20 failed
📁 Fix files reverted (1 files)
  • src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs

📱 UI Tests — CarouselView,CollectionView,IndicatorView

Detected UI test categories: CarouselView,CollectionView,IndicatorView

Deep UI tests — 545 passed, 0 failed across 3 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
CarouselView 94/97 ✓
CollectionView 424/427 ✓
IndicatorView 27/27 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

📋 Pre-Flight — Context & Validation

Issue: #29312 - [iOS] - CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2
PR: #36287 - [iOS/Mac] Fix CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2
Platforms Affected: iOS, MacCatalyst
Files Changed: 1 implementation, 1 test

Key Findings

  • The bug is specific to CarouselView2 on iOS/MacCatalyst: CarouselView.Position can be updated while the handler is detached, but reattach previously restored from stale CurrentItem.
  • PR #36287 tracks _lastSyncedPosition and cancels pending iOS 26 debounce callbacks, but the current iOS 26 delayed path can still ignore valid position updates when CurrentItem is null/unindexable.
  • The linked issue explicitly asks to enable CarouselViewUpdateCurrentItem; the PR instead enables a broader IndicatorViewFeatureTests.VerifyIndicatorView_Position test, leaving direct CV2 repro coverage uncertain.
  • GitHub CLI auth is unavailable in this environment, so PR context was fetched through public GitHub API and required-check status could not be fully classified locally.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: low
Errors: 2 | Warnings: 1 | Suggestions: 0

Key code review findings:

  • src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs:664 — iOS 26 guard returns when CurrentItem is null/unindexable, dropping valid Position updates and leaving CarouselView/IndicatorView unsynced.
  • ✗ Gate failed in prior review: target IndicatorView UI test did not pass with the PR fix.
  • src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:82 — enabled test does not directly cover the linked CarouselViewUpdateCurrentItem/Issue9827 CV2 repro.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #36287 Track _lastSyncedPosition across detach/reattach, compare it to Position during UpdateInitialPosition, cancel iOS 26 pending debounce in TearDown, and enable VerifyIndicatorView_Position on iOS/MacCatalyst. ❌ FAILED (Gate) CarouselViewController2.cs, IndicatorViewFeatureTests.cs Original PR; prior gate says tests still failed and review found unresolved iOS 26 null-CurrentItem path.

🔬 Code Review — Deep Analysis

Code Review — PR #36287

Independent Assessment

What this changes: iOS/MacCatalyst CarouselView2 now tracks last synced Position, uses it during detach/reattach reconciliation, cancels pending iOS 26 scroll debounce on teardown, and enables an IndicatorView position UI test on iOS/Catalyst.
Inferred motivation: Preserve Position/IndicatorView updates made while the handler is detached.

Reconciliation with PR Narrative

Author claims: Fixes #29312 by honoring detached Position changes and preventing stale iOS 26 debounce callbacks.
Agreement/disagreement: Mostly aligned, but the iOS 26 guard can suppress valid Position updates when CurrentItem is null.

Prior Review Reconciliation

Prior ❌ Error Finding Source Status Evidence
iOS 26 guard drops valid Position updates when CurrentItem is null MauiBot ❌ Unresolved Guard remains in CarouselViewController2.cs:664-667.
Gate failed: target tests still failed with fix MauiBot ❌ Unresolved Same PR head; no evidence of green rerun.

Blast Radius Assessment

  • Runs for all instances: Yes — all iOS/MacCatalyst CarouselView2 position updates.
  • Startup impact: No direct startup path.
  • Static/shared state: No static state; instance lifecycle state only.

CI Status

  • Required-check result: undetermined via gh pr checks --required because gh is unauthenticated.
  • Public check-run fallback: 8 failing checks, including maui-pr-uitests and maui-pr-devicetests.
  • Classification: red; prior /review tests classified two macOS DragAndDrop failures as likely PR-caused.
  • Action taken: invoked azdo-build-investigator; ci-analysis unavailable; confidence capped low.

Findings

❌ Error — iOS 26 guard drops valid Position updates

src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs:664

The new early return skips ScrollToPosition whenever CurrentItem is null/unindexable. A valid Position change after an ItemsSource/ViewModel swap can therefore be ignored on iOS 26, leaving CarouselView/IndicatorView unsynced.

⚠️ Warning — Regression test does not cover linked repro directly

src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs:82

Issue #29312 explicitly calls out CarouselViewUpdateCurrentItem / Issue9827. This PR enables a broader IndicatorView position test, but the existing repro still uses CarouselView1, not Items2/CV2.

Failure-Mode Probing

  • CurrentItem == null, valid Position > 0: iOS 26 path returns without scrolling.
  • Detach/reconnect: _lastSyncedPosition can preserve detached updates, but only if later sync is not skipped.
  • Pending debounce after teardown: cancellation in TearDown/Dispose addresses stale callback risk.

Verdict: NEEDS_CHANGES

Confidence: low
Summary: The lifecycle approach is plausible, but an unresolved prior ❌ finding remains in current code and CI is red/undetermined. Fix the iOS 26 null-CurrentItem path and add direct CV2 repro coverage before merge.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Queue explicit detached Position updates and replay them in UpdateInitialPosition. ❌ FAIL 1 file Built/deployed after build-task recovery, but full IndicatorViewFeatureTests failed with 18 screenshot-baseline mismatches; direct position test was not enabled, so validation was inconclusive.
2 try-fix Synchronize CurrentItem from Position early in mapper/controller so existing reattach CurrentItem preference works. ⚠ PARTIAL 3 files VerifyIndicatorView_Position passed; full gate failed with unrelated screenshot mismatches. Risk: earlier CurrentItem binding updates.
3 try-fix Reattach-only policy: prefer Position if returning from detach and ItemsSource ref/count did not change. ⚠ PARTIAL — best candidate 2 files VerifyIndicatorView_Position and VerifyIndicatorView_Count passed; full gate failed with unrelated screenshot mismatches. Narrowest behavioral blast radius among passing focused candidates.
4 try-fix Per-property mapper versioning: reconcile detached Position vs CurrentItem by most recent virtual-property change. ⚠ PARTIAL 3 files VerifyIndicatorView_Position and VerifyIndicatorView_Count passed; full gate failed with unrelated screenshot mismatches. More robust for detached CurrentItem changes but higher state/mapper complexity.
PR PR #36287 Track _lastSyncedPosition, compare on reattach, cancel iOS 26 debounce, and guard invalid CurrentItem callback. ❌ FAILED (Gate) 2 files Original PR; review found iOS 26 invalid-CurrentItem guard can drop valid Position updates.

Cross-Pollination

Model Round New Ideas? Details
maui-expert-reviewer 1 Yes Suggested pending explicit Position queue (try-fix-1).
maui-expert-reviewer 2 Yes Suggested early CurrentItem synchronization from Position (try-fix-2).
maui-expert-reviewer 3 Yes Suggested reattach-only Position precedence when ItemsSource unchanged (try-fix-3).
maui-expert-reviewer 4 Yes Suggested per-property version reconciliation (try-fix-4).
maui-expert-reviewer Final No No remaining non-trivial ideas beyond try-fix-3/4; remaining full-gate failures are screenshot mismatches, not position logic.

Exhausted: Yes
Selected Fix: Candidate #3, conditionally — it is the narrowest alternative that passed focused VerifyIndicatorView_Position plus adjacent VerifyIndicatorView_Count, avoids PR #36287's _lastSyncedPosition inference and invalid-CurrentItem iOS 26 guard, and avoids try-fix-2/4 mapper-side behavioral expansion. It is not a full green candidate because the full IndicatorViewFeatureTests gate remained red from broad screenshot-baseline mismatches in every attempt.

Shared Test Failure Pattern

The iOS UI-test environment was usable: build tasks were rebuilt once, the HostApp built/deployed, and focused UI tests ran. The class-level IndicatorViewFeatureTests command repeatedly failed because unrelated screenshot tests differed from baselines by about 2.03%. Focused non-screenshot tests are therefore the reliable discriminator for this issue in this run.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the current description accurately describes the raw PR's _lastSyncedPosition approach, but the winning candidate is try-fix-3, which uses reattach-scoped ItemsSource snapshot logic instead.

Recommended title

[iOS/Mac] CarouselView: Preserve detached Position updates in CV2

Recommended description

### Issue Details:

The position and the IndicatorView don't update to reflect the current position in the CarouselView.

### Root Cause

- The issue occurs only in a two-page navigation flow, where `CarouselView.Position` is updated while the CarouselView handler is detached (for example, after navigating to another page).
- During this time, the managed `Position` property is updated through data binding, but `CurrentItem` remains unchanged because it is only synchronized while the handler is attached. When the page is navigated back to, the existing reattachment logic always restored the carousel based on `CurrentItem`, effectively treating it as the source of truth. As a result, any `Position` changes made while detached were ignored, leaving the CarouselView, IndicatorView, and bound Position state out of sync.
- A second issue was observed on iOS 26, where a pending `_scrollDebounce` callback could still execute after the controller was torn down. This stale callback could invoke scroll work using outdated state after the controller detached.

### Description of Change

- During detach, snapshot the current `ItemsSource` reference and native item count.
- During reattachment, determine whether the CarouselView is returning from detach and whether the `ItemsSource` reference/count changed while detached.
- If the `ItemsSource` did not change and the current `Position` is valid but disagrees with `CurrentItem`, treat `Position` as the source of truth and restore the carousel to that position.
- If the `ItemsSource` changed while detached, preserve the existing `CurrentItem`-based restoration behavior so collection mutation scenarios continue to keep the intended item visible.
- Cancel and dispose pending `_scrollDebounce` work during `TearDown`, preventing delayed iOS 26 callbacks from executing after the controller has detached.
- Enable `IndicatorViewFeatureTests.VerifyIndicatorView_Position` on iOS and MacCatalyst while keeping the Windows exclusion for #29312 and preserving the separate #15443 exclusions for other IndicatorView tests.

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

### Issues Fixed:
Fixes #29312

### Screenshots
| Before  | After |
|---------|--------|
| <video src="https://github.com/user-attachments/assets/b0ac0a55-1322-4fa4-ab9d-66c9351b05af"> | <video src="https://github.com/user-attachments/assets/feffbd82-d626-41a4-9b32-975461781045"> |

🏁 Report — Final Recommendation

Comparative Candidate Report — PR #36287

Ranking

Rank Candidate Regression evidence Assessment
1 try-fix-3 Focused VerifyIndicatorView_Position ✅ PASS; adjacent VerifyIndicatorView_Count ✅ PASS; full IndicatorViewFeatureTests ❌ failed from repeated unrelated screenshot-baseline mismatches Best candidate. It scopes the behavior change to detach/reattach, avoids early mapper-side CurrentItem updates, cancels stale debounce work, and preserves existing CurrentItem behavior when the ItemsSource ref/count changed while detached.
2 try-fix-4 Focused VerifyIndicatorView_Position ✅ PASS; adjacent VerifyIndicatorView_Count ✅ PASS; full class ❌ same screenshot-baseline failures Robust "last changed virtual property wins" model, but it adds mapper-version state across both Position and CurrentItem. That is more complex and risks renderer-originated updates polluting the ordering.
3 try-fix-2 Focused VerifyIndicatorView_Position ✅ PASS; full class ❌ same screenshot-baseline failures Fixes the direct symptom by syncing CurrentItem from Position early, but that expands behavior beyond reattach and may fire two-way CurrentItem binding updates earlier than previous behavior.
4 pr-plus-reviewer No focused pass recorded for this adjusted candidate; raw PR gate failed Improves the PR by fixing the expert-reviewer iOS 26 null/unindexable CurrentItem path, but it still relies on _lastSyncedPosition inference and retains the prior concern around UpdateItemsSource resetting _lastSyncedPosition to 0 when the controller may not have synced that position.
5 try-fix-1 Full gate ❌ FAIL; direct VerifyIndicatorView_Position was not enabled in that candidate Queuing explicit detached Position updates is conceptually direct, but validation was inconclusive/failing and it depends on UpdateFromPosition() being delivered while detached.
6 pr Gate ❌ FAILED; expert reviewer found a major actionable issue Lowest ranked. It can still drop valid iOS 26 Position updates when CurrentItem is null/unindexable, and the supplied gate result says tests did not behave as expected.

Winning candidate: try-fix-3

try-fix-3 wins because it is the narrowest candidate with focused evidence that the relevant iOS position regression passes. Its reattach-only policy directly addresses the two-page navigation scenario: when the view comes back after detach and the ItemsSource reference/count did not change, a valid Position disagreement is treated as intentional and restored; when the ItemsSource did change, existing CurrentItem-based preservation remains preferred.

The full class gate failed for all runnable alternatives due repeated screenshot-baseline mismatches around 2.03%, so those failures do not distinguish the candidate logic. The required ordering rule is still satisfied: candidates with the raw failed gate and no focused pass (pr, try-fix-1) are ranked below candidates with focused passing regression evidence.

Candidate notes

pr adds useful cleanup by cancelling _scrollDebounce in TearDown, but its new iOS 26 guard returns on invalid CurrentItem and can ignore a valid Position update. The _lastSyncedPosition model also depends on the field always representing a truly synced position, which is questionable after UpdateItemsSource.

pr-plus-reviewer fixes the concrete inline finding by preserving valid Position in the delayed iOS 26 path when CurrentItem is temporarily unavailable. It is still not the best candidate because it retains the broader _lastSyncedPosition source-of-truth inference and lacks the focused passing validation that try-fix-3 has.

try-fix-2 and try-fix-4 are viable but broader. try-fix-2 changes mapper behavior by synchronizing CurrentItem before the native view is ready; try-fix-4 models detached property recency explicitly but adds more lifecycle state and relies on mapper-version semantics not being polluted by renderer updates.

try-fix-1 is below the focused-pass candidates because its validation did not include the direct position test and the full class run failed.


🧭 Next Steps — alternative fix proposed (try-fix-3)

Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-3 as the strongest fix.

Why: try-fix-3 is the narrowest candidate with focused iOS evidence: VerifyIndicatorView_Position and the adjacent Count test passed, while the full class failures were unrelated screenshot-baseline mismatches. It avoids the PR fix's invalid-CurrentItem iOS 26 drop path and avoids broader mapper-side synchronization changes.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (try-fix-3)
diff --git a/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs b/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs
index d97a09dec9..d5bae99f93 100644
--- a/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs
+++ b/src/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cs
@@ -21,6 +21,8 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
 		bool _wasDetachedFromWindow = false;
 		CarouselViewLoopManager _carouselViewLoopManager;
 		CancellationTokenSource _scrollDebounce;
+		object _itemsSourceOnDetach;
+		int _itemsCountOnDetach = -1;
 		NSObject _orientationObserver;
 
 		// We need to keep track of the old views to update the visual states
@@ -160,17 +162,22 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
 
 		private protected override async void AttachingToWindow()
 		{
+			var wasDetachedFromWindow = _wasDetachedFromWindow;
 			base.AttachingToWindow();
 			Setup(ItemsView);
+			var itemsSourceChangedWhileDetached =
+				wasDetachedFromWindow && DidItemsSourceChangeSinceDetach();
+
 			// Refresh the current visible items after setup to catch any ItemsSource changes that occurred on other pages
 			// This ensures that updates made on other pages are reflected when navigating back
-			if (_wasDetachedFromWindow)
+			if (wasDetachedFromWindow)
 			{
 				RefreshVisibleItems();
 			}
 			_wasDetachedFromWindow = false;
+			ClearItemsSourceDetachSnapshot();
 			// if we navigate back on NavigationController LayoutSubviews might not fire.
-			await UpdateInitialPosition();
+			await UpdateInitialPosition(wasDetachedFromWindow, itemsSourceChangedWhileDetached);
 		}
 
 		void RefreshVisibleItems()
@@ -191,17 +198,42 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
 		private protected override void DetachingFromWindow()
 		{
 			_wasDetachedFromWindow = true;
+			CaptureItemsSourceDetachSnapshot();
 			base.DetachingFromWindow();
 			TearDown(ItemsView);
 		}
 
 		internal bool InitialPositionSet { get; private set; }
 
+		void CaptureItemsSourceDetachSnapshot()
+		{
+			_itemsSourceOnDetach = ItemsView?.ItemsSource;
+			_itemsCountOnDetach = ItemsSource?.ItemCount ?? -1;
+		}
+
+		bool DidItemsSourceChangeSinceDetach()
+		{
+			var currentItemsSource = ItemsView?.ItemsSource;
+			var currentCount = ItemsSource?.ItemCount ?? -1;
+			return !ReferenceEquals(_itemsSourceOnDetach, currentItemsSource) ||
+				_itemsCountOnDetach != currentCount;
+		}
+
+		void ClearItemsSourceDetachSnapshot()
+		{
+			_itemsSourceOnDetach = null;
+			_itemsCountOnDetach = -1;
+		}
+
 		void TearDown(CarouselView carouselView)
 		{
 			_oldViews = null;
 			InitialPositionSet = false;
 
+			_scrollDebounce?.Cancel();
+			_scrollDebounce?.Dispose();
+			_scrollDebounce = null;
+
 			UnsubscribeCollectionItemsSourceChanged(ItemsSource);
 			// Remove the block-based observer using the stored token (RemoveObserver(this,...) does not remove block observers).
 			if (_orientationObserver is not null)
@@ -658,7 +690,7 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
 			// SetCurrentItem(carouselPosition);
 		}
 
-		async Task UpdateInitialPosition()
+		async Task UpdateInitialPosition(bool wasDetachedFromWindow = false, bool itemsSourceChangedWhileDetached = false)
 		{
 			if (ItemsView is not CarouselView carousel)
 			{
@@ -700,15 +732,17 @@ namespace Microsoft.Maui.Controls.Handlers.Items2
 
 					int position = carousel.Position;
 					var currentItem = carousel.CurrentItem;
-
-					if (currentItem != null)
+					var currentItemPosition = currentItem is not null
+						? ItemsSource.GetIndexForItem(currentItem).Row
+						: -1;
+					bool positionIsValid = position >= 0 && position < ItemsSource.ItemCount;
+					bool preferPositionOnReattach = wasDetachedFromWindow &&
+						!itemsSourceChangedWhileDetached && positionIsValid &&
+						(currentItem is null || currentItemPosition != position);
+
+					if (!preferPositionOnReattach && currentItemPosition != -1)
 					{
-						// Sometimes the item could be just being removed while we navigate back to the CarouselView
-						var positionCurrentItem = ItemsSource.GetIndexForItem(currentItem).Row;
-						if (positionCurrentItem != -1)
-						{
-							position = positionCurrentItem;
-						}
+						position = currentItemPosition;
 					}
 
 					var projectedPosition = LoopItemsSource.Loop
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs
index ede4f3b91e..da4c658686 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/IndicatorViewFeatureTests.cs
@@ -79,7 +79,7 @@ public class IndicatorViewFeatureTests : _GalleryUITest
 		Assert.That(App.FindElement(CountLabel).GetText(), Is.EqualTo("Count: 5"));
 	}
 
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/29312, https://github.com/dotnet/maui/issues/15443
+#if TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/29312
 	[Test, Order(4)]
 	[Category(UITestCategories.IndicatorView)]
 	public void VerifyIndicatorView_Position()
@@ -93,7 +93,9 @@ public class IndicatorViewFeatureTests : _GalleryUITest
 		App.Tap(Apply);
 		Assert.That(App.FindElement(PositionLabel).GetText(), Is.EqualTo("Position: 1"));
 	}
+#endif
 
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS // Issue Link: https://github.com/dotnet/maui/issues/15443
 	[Test, Order(13)]
 	[Category(UITestCategories.IndicatorView)]
 	public void VerifySelectedIndicatorColorWhenItemsChanged()

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 15, 2026
@kubaflo
kubaflo changed the base branch from main to inflight/current July 15, 2026 13:55
@kubaflo
kubaflo merged commit 168830f into dotnet:inflight/current Jul 15, 2026
2 of 11 checks passed
@github-actions github-actions Bot added this to the .NET 10 SR10 milestone Jul 15, 2026
kubaflo added a commit that referenced this pull request Jul 15, 2026
…en Moving to Next Item in CV2 (#36287)

### Issue Details:

The position and the IndicatorView don't update to reflect the current
position in the CarouselView.


### Root Cause

- The issue occurs only in a two-page navigation flow, where
CarouselView.Position is updated while the CarouselView handler is
detached (for example, after navigating to another page).

- During this time, the managed Position property is updated through
data binding, but CurrentItem remains unchanged because it is only
synchronized while the handler is attached. When the page is navigated
back to, the existing reattachment logic always restored the carousel
based on CurrentItem, effectively treating it as the source of truth. As
a result, any Position changes made while detached were ignored, leaving
the CarouselView, IndicatorView, and bound Position state out of sync.
- A second issue was observed on iOS 26, where a pending _scrollDebounce
callback could still execute after the controller was torn down. This
stale callback could invoke ScrollToPosition using outdated state and
overwrite the internally tracked position, causing the carousel position
to drift across navigation cycles.

### Description of Change

- Introduced _lastSyncedPosition to track the last position that was
successfully synchronized between the controller and the CarouselView.
- During reattachment, the handler now determines whether Position
changed while it was detached by comparing the current Position with
_lastSyncedPosition.
- If Position changed while detached, it is treated as the source of
truth and the carousel is restored to the updated position.
- Otherwise, the existing CurrentItem-based restoration is preserved,
which correctly handles scenarios such as collection changes while the
page was inactive.
- This ensures that explicit Position updates made through bindings or
commands while the handler is detached are honored when the page is
shown again, while preserving the previous behavior for collection
mutation scenarios.
- Resolved a secondary synchronization issue by cancelling and disposing
the pending _scrollDebounce operation during TearDown, preventing
delayed scroll callbacks from executing after the controller has been
detached.
- Additionally, a guard was added to ignore debounce callbacks when the
resolved CurrentItem position is invalid, preventing stale callbacks
from invoking ScrollToPosition and overwriting the tracked synchronized
position. This eliminates the position drift observed on repeated
navigation cycles on iOS 26.

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

### Issues Fixed:
Fixes #29312 

### Screenshots
| Before  | After |
|---------|--------|
| <video
src="https://github.com/user-attachments/assets/b0ac0a55-1322-4fa4-ab9d-66c9351b05af">
| <video
src="https://github.com/user-attachments/assets/feffbd82-d626-41a4-9b32-975461781045">
|

---------

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jul 20, 2026
…r off-screen ItemsSource Change (#36681)

<!-- 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 an issue with `CarouselView` on iOS where
replacing the `ItemsSource` and setting a new `CurrentItem` while the
view is detached could result in the wrong item being displayed when
reattached. The update ensures that the new `CurrentItem` is respected
over any stale `Position` value after the `ItemsSource` is replaced
while detached. Additionally, a new test is added to prevent regressions
for this scenario.

This is a regression introduced by #36287, which added
`_lastSyncedPosition` tracking and a reattach heuristic in
`CarouselViewController2.cs`. That change unconditionally reset
`_lastSyncedPosition` on every `ItemsSource` update — even while
detached, when the `Position`/`CurrentItem` reset is skipped — causing
the stale `Position` to be wrongly treated as an explicit user change on
reattach and overriding a valid `CurrentItem` set on the replacement
source.

### Description of change
**Bug Fixes:**

* Updated `CarouselViewController2.cs` to reset the tracked position
correctly when the `ItemsSource` is replaced, ensuring the new
`CurrentItem` is honored over a stale `Position` after reattaching the
view. This addresses the regression introduced in #36287.

**Testing:**

* Added a new test
`CarouselViewHonorsCurrentItemOverStalePositionAfterDetachedItemsSourceReplacement`
in `CarouselViewTests.iOS.cs` to verify that replacing the `ItemsSource`
and setting a new `CurrentItem` while detached works as expected after
reattaching.
* Introduced a helper method
`SetupBuilderForDetachedItemsSourceReplacement` to configure handlers
for the test scenario.

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

### Tested the behavior in the following platforms

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


| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/881dbe8f-323b-49ba-894d-f23fd368651c">
| <video
src="https://github.com/user-attachments/assets/e55b5e56-4d74-4ad5-83b9-d63b41c6e503">
|

<!--

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
kubaflo added a commit that referenced this pull request Jul 22, 2026
…en Moving to Next Item in CV2 (#36287)

### Issue Details:

The position and the IndicatorView don't update to reflect the current
position in the CarouselView.


### Root Cause

- The issue occurs only in a two-page navigation flow, where
CarouselView.Position is updated while the CarouselView handler is
detached (for example, after navigating to another page).

- During this time, the managed Position property is updated through
data binding, but CurrentItem remains unchanged because it is only
synchronized while the handler is attached. When the page is navigated
back to, the existing reattachment logic always restored the carousel
based on CurrentItem, effectively treating it as the source of truth. As
a result, any Position changes made while detached were ignored, leaving
the CarouselView, IndicatorView, and bound Position state out of sync.
- A second issue was observed on iOS 26, where a pending _scrollDebounce
callback could still execute after the controller was torn down. This
stale callback could invoke ScrollToPosition using outdated state and
overwrite the internally tracked position, causing the carousel position
to drift across navigation cycles.

### Description of Change

- Introduced _lastSyncedPosition to track the last position that was
successfully synchronized between the controller and the CarouselView.
- During reattachment, the handler now determines whether Position
changed while it was detached by comparing the current Position with
_lastSyncedPosition.
- If Position changed while detached, it is treated as the source of
truth and the carousel is restored to the updated position.
- Otherwise, the existing CurrentItem-based restoration is preserved,
which correctly handles scenarios such as collection changes while the
page was inactive.
- This ensures that explicit Position updates made through bindings or
commands while the handler is detached are honored when the page is
shown again, while preserving the previous behavior for collection
mutation scenarios.
- Resolved a secondary synchronization issue by cancelling and disposing
the pending _scrollDebounce operation during TearDown, preventing
delayed scroll callbacks from executing after the controller has been
detached.
- Additionally, a guard was added to ignore debounce callbacks when the
resolved CurrentItem position is invalid, preventing stale callbacks
from invoking ScrollToPosition and overwriting the tracked synchronized
position. This eliminates the position drift observed on repeated
navigation cycles on iOS 26.

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

### Issues Fixed:
Fixes #29312 

### Screenshots
| Before  | After |
|---------|--------|
| <video
src="https://github.com/user-attachments/assets/b0ac0a55-1322-4fa4-ab9d-66c9351b05af">
| <video
src="https://github.com/user-attachments/assets/feffbd82-d626-41a4-9b32-975461781045">
|

---------

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jul 22, 2026
…r off-screen ItemsSource Change (#36681)

<!-- 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 an issue with `CarouselView` on iOS where
replacing the `ItemsSource` and setting a new `CurrentItem` while the
view is detached could result in the wrong item being displayed when
reattached. The update ensures that the new `CurrentItem` is respected
over any stale `Position` value after the `ItemsSource` is replaced
while detached. Additionally, a new test is added to prevent regressions
for this scenario.

This is a regression introduced by #36287, which added
`_lastSyncedPosition` tracking and a reattach heuristic in
`CarouselViewController2.cs`. That change unconditionally reset
`_lastSyncedPosition` on every `ItemsSource` update — even while
detached, when the `Position`/`CurrentItem` reset is skipped — causing
the stale `Position` to be wrongly treated as an explicit user change on
reattach and overriding a valid `CurrentItem` set on the replacement
source.

### Description of change
**Bug Fixes:**

* Updated `CarouselViewController2.cs` to reset the tracked position
correctly when the `ItemsSource` is replaced, ensuring the new
`CurrentItem` is honored over a stale `Position` after reattaching the
view. This addresses the regression introduced in #36287.

**Testing:**

* Added a new test
`CarouselViewHonorsCurrentItemOverStalePositionAfterDetachedItemsSourceReplacement`
in `CarouselViewTests.iOS.cs` to verify that replacing the `ItemsSource`
and setting a new `CurrentItem` while detached works as expected after
reattaching.
* Introduced a helper method
`SetupBuilderForDetachedItemsSourceReplacement` to configure handlers
for the test scenario.

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

### Tested the behavior in the following platforms

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


| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/881dbe8f-323b-49ba-894d-f23fd368651c">
| <video
src="https://github.com/user-attachments/assets/e55b5e56-4d74-4ad5-83b9-d63b41c6e503">
|

<!--

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
kubaflo added a commit that referenced this pull request Jul 28, 2026
…en Moving to Next Item in CV2 (#36287)

### Issue Details:

The position and the IndicatorView don't update to reflect the current
position in the CarouselView.


### Root Cause

- The issue occurs only in a two-page navigation flow, where
CarouselView.Position is updated while the CarouselView handler is
detached (for example, after navigating to another page).

- During this time, the managed Position property is updated through
data binding, but CurrentItem remains unchanged because it is only
synchronized while the handler is attached. When the page is navigated
back to, the existing reattachment logic always restored the carousel
based on CurrentItem, effectively treating it as the source of truth. As
a result, any Position changes made while detached were ignored, leaving
the CarouselView, IndicatorView, and bound Position state out of sync.
- A second issue was observed on iOS 26, where a pending _scrollDebounce
callback could still execute after the controller was torn down. This
stale callback could invoke ScrollToPosition using outdated state and
overwrite the internally tracked position, causing the carousel position
to drift across navigation cycles.

### Description of Change

- Introduced _lastSyncedPosition to track the last position that was
successfully synchronized between the controller and the CarouselView.
- During reattachment, the handler now determines whether Position
changed while it was detached by comparing the current Position with
_lastSyncedPosition.
- If Position changed while detached, it is treated as the source of
truth and the carousel is restored to the updated position.
- Otherwise, the existing CurrentItem-based restoration is preserved,
which correctly handles scenarios such as collection changes while the
page was inactive.
- This ensures that explicit Position updates made through bindings or
commands while the handler is detached are honored when the page is
shown again, while preserving the previous behavior for collection
mutation scenarios.
- Resolved a secondary synchronization issue by cancelling and disposing
the pending _scrollDebounce operation during TearDown, preventing
delayed scroll callbacks from executing after the controller has been
detached.
- Additionally, a guard was added to ignore debounce callbacks when the
resolved CurrentItem position is invalid, preventing stale callbacks
from invoking ScrollToPosition and overwriting the tracked synchronized
position. This eliminates the position drift observed on repeated
navigation cycles on iOS 26.

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

### Issues Fixed:
Fixes #29312 

### Screenshots
| Before  | After |
|---------|--------|
| <video
src="https://github.com/user-attachments/assets/b0ac0a55-1322-4fa4-ab9d-66c9351b05af">
| <video
src="https://github.com/user-attachments/assets/feffbd82-d626-41a4-9b32-975461781045">
|

---------

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jul 28, 2026
…r off-screen ItemsSource Change (#36681)

<!-- 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 an issue with `CarouselView` on iOS where
replacing the `ItemsSource` and setting a new `CurrentItem` while the
view is detached could result in the wrong item being displayed when
reattached. The update ensures that the new `CurrentItem` is respected
over any stale `Position` value after the `ItemsSource` is replaced
while detached. Additionally, a new test is added to prevent regressions
for this scenario.

This is a regression introduced by #36287, which added
`_lastSyncedPosition` tracking and a reattach heuristic in
`CarouselViewController2.cs`. That change unconditionally reset
`_lastSyncedPosition` on every `ItemsSource` update — even while
detached, when the `Position`/`CurrentItem` reset is skipped — causing
the stale `Position` to be wrongly treated as an explicit user change on
reattach and overriding a valid `CurrentItem` set on the replacement
source.

### Description of change
**Bug Fixes:**

* Updated `CarouselViewController2.cs` to reset the tracked position
correctly when the `ItemsSource` is replaced, ensuring the new
`CurrentItem` is honored over a stale `Position` after reattaching the
view. This addresses the regression introduced in #36287.

**Testing:**

* Added a new test
`CarouselViewHonorsCurrentItemOverStalePositionAfterDetachedItemsSourceReplacement`
in `CarouselViewTests.iOS.cs` to verify that replacing the `ItemsSource`
and setting a new `CurrentItem` while detached works as expected after
reattaching.
* Introduced a helper method
`SetupBuilderForDetachedItemsSourceReplacement` to configure handlers
for the test scenario.

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

### Tested the behavior in the following platforms

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


| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/881dbe8f-323b-49ba-894d-f23fd368651c">
| <video
src="https://github.com/user-attachments/assets/e55b5e56-4d74-4ad5-83b9-d63b41c6e503">
|

<!--

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
kubaflo added a commit that referenced this pull request Jul 29, 2026
…en Moving to Next Item in CV2 (#36287)

### Issue Details:

The position and the IndicatorView don't update to reflect the current
position in the CarouselView.


### Root Cause

- The issue occurs only in a two-page navigation flow, where
CarouselView.Position is updated while the CarouselView handler is
detached (for example, after navigating to another page).

- During this time, the managed Position property is updated through
data binding, but CurrentItem remains unchanged because it is only
synchronized while the handler is attached. When the page is navigated
back to, the existing reattachment logic always restored the carousel
based on CurrentItem, effectively treating it as the source of truth. As
a result, any Position changes made while detached were ignored, leaving
the CarouselView, IndicatorView, and bound Position state out of sync.
- A second issue was observed on iOS 26, where a pending _scrollDebounce
callback could still execute after the controller was torn down. This
stale callback could invoke ScrollToPosition using outdated state and
overwrite the internally tracked position, causing the carousel position
to drift across navigation cycles.

### Description of Change

- Introduced _lastSyncedPosition to track the last position that was
successfully synchronized between the controller and the CarouselView.
- During reattachment, the handler now determines whether Position
changed while it was detached by comparing the current Position with
_lastSyncedPosition.
- If Position changed while detached, it is treated as the source of
truth and the carousel is restored to the updated position.
- Otherwise, the existing CurrentItem-based restoration is preserved,
which correctly handles scenarios such as collection changes while the
page was inactive.
- This ensures that explicit Position updates made through bindings or
commands while the handler is detached are honored when the page is
shown again, while preserving the previous behavior for collection
mutation scenarios.
- Resolved a secondary synchronization issue by cancelling and disposing
the pending _scrollDebounce operation during TearDown, preventing
delayed scroll callbacks from executing after the controller has been
detached.
- Additionally, a guard was added to ignore debounce callbacks when the
resolved CurrentItem position is invalid, preventing stale callbacks
from invoking ScrollToPosition and overwriting the tracked synchronized
position. This eliminates the position drift observed on repeated
navigation cycles on iOS 26.

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

### Issues Fixed:
Fixes #29312 

### Screenshots
| Before  | After |
|---------|--------|
| <video
src="https://github.com/user-attachments/assets/b0ac0a55-1322-4fa4-ab9d-66c9351b05af">
| <video
src="https://github.com/user-attachments/assets/feffbd82-d626-41a4-9b32-975461781045">
|

---------

Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jul 29, 2026
…r off-screen ItemsSource Change (#36681)

<!-- 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 an issue with `CarouselView` on iOS where
replacing the `ItemsSource` and setting a new `CurrentItem` while the
view is detached could result in the wrong item being displayed when
reattached. The update ensures that the new `CurrentItem` is respected
over any stale `Position` value after the `ItemsSource` is replaced
while detached. Additionally, a new test is added to prevent regressions
for this scenario.

This is a regression introduced by #36287, which added
`_lastSyncedPosition` tracking and a reattach heuristic in
`CarouselViewController2.cs`. That change unconditionally reset
`_lastSyncedPosition` on every `ItemsSource` update — even while
detached, when the `Position`/`CurrentItem` reset is skipped — causing
the stale `Position` to be wrongly treated as an explicit user change on
reattach and overriding a valid `CurrentItem` set on the replacement
source.

### Description of change
**Bug Fixes:**

* Updated `CarouselViewController2.cs` to reset the tracked position
correctly when the `ItemsSource` is replaced, ensuring the new
`CurrentItem` is honored over a stale `Position` after reattaching the
view. This addresses the regression introduced in #36287.

**Testing:**

* Added a new test
`CarouselViewHonorsCurrentItemOverStalePositionAfterDetachedItemsSourceReplacement`
in `CarouselViewTests.iOS.cs` to verify that replacing the `ItemsSource`
and setting a new `CurrentItem` while detached works as expected after
reattaching.
* Introduced a helper method
`SetupBuilderForDetachedItemsSourceReplacement` to configure handlers
for the test scenario.

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

### Tested the behavior in the following platforms

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


| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/881dbe8f-323b-49ba-894d-f23fd368651c">
| <video
src="https://github.com/user-attachments/assets/e55b5e56-4d74-4ad5-83b9-d63b41c6e503">
|

<!--

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-collectionview CollectionView, CarouselView, IndicatorView partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-failed AI could not verify tests catch the bug s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] - CarouselView Position and IndicatorView Not Updating When Moving to Next Item in CV2

5 participants