Fix LayoutCycleException from nested Borders on Windows#34337
Fix LayoutCycleException from nested Borders on Windows#34337kubaflo merged 4 commits intodotnet:inflight/currentfrom
Conversation
Skip PlatformArrange shape update on Windows where ContentPanel.SizeChanged already handles size-based path geometry updates. Calling UpdateValue(Shape) during PlatformArrange sets Path.Data during the WinUI arrange pass, which invalidates layout. With many nested Borders (300+ at 3+ depth), cascading invalidations exceed WinUI's layout cycle detection threshold. Regression from PR dotnet#24844 which moved shape updates from OnPropertyChanged (Width/Height) to PlatformArrange. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Creates 350 Border elements at 3 nesting levels to verify no LayoutCycleException occurs. Tests the fix on Windows where deep Border trees previously caused crashes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34337Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34337" |
|
Hey there @@Oxymoron290! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
@dotnet-policy-service agree company="Microsoft" |
There was a problem hiding this comment.
Pull request overview
This PR addresses a Windows-specific LayoutCycleException caused by deeply nested Border elements by removing the extra shape-geometry update performed during the WinUI arrange pass, relying instead on the existing ContentPanel size-changed path update logic.
Changes:
- Skip
BorderHandler.PlatformArrangeshape updates on Windows to avoid layout invalidation loops during arrange. - Record the Windows TFM API surface change in
PublicAPI.Unshipped.txt. - Add a HostApp repro page and an Appium UI test for issue #32406.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Core/src/Handlers/Border/BorderHandler.cs | Excludes the PlatformArrange override (and _lastSize) on Windows to prevent re-triggering layout during arrange. |
| src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Marks the removed Windows-specific override as a removed API entry. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs | Adds a repro page that constructs a large nested Border tree and signals success on load. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs | Adds a UI test that asserts the page loads successfully without crashing. |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs
Outdated
Show resolved
Hide resolved
- Move rationale comment above #if !WINDOWS so it's visible in Windows builds - Fix test namespace to Microsoft.Maui.TestCases.Tests.Issues and add required usings - Clarify element count comment (350 elements * 3 depth = 1050 total Borders) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Oxymoron290
left a comment
There was a problem hiding this comment.
Addressed all 3 review comments in commit aac7663:
- Comment placement: Moved the rationale comment above the #if directive so it is visible in Windows builds.
- Test namespace/usings: Fixed namespace to Microsoft.Maui.TestCases.Tests.Issues and added required using directives.
- Element count clarity: Updated comment to clarify that 350 elements x 3 nesting depth = 1050 total Border instances, intentional to exceed the layout cycle threshold.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs
Outdated
Show resolved
Hide resolved
- Keep PlatformArrange public API surface consistent across all platforms; conditionalize only the UpdateValue(Shape) body with #if !WINDOWS - Revert PublicAPI.Unshipped.txt removal entry (no longer needed) - Use WaitForTextToBePresentInElement instead of WaitForElement + GetText to avoid race between element existence and Loaded handler completion Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run maui-pr-uitests , maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34337 | Keep PlatformArrange override, but on Windows avoid the arrange-time UpdateValue(Shape) because ContentPanel.ContentPanelSizeChanged already handles size-based shape updates |
PENDING (Gate) | src/Core/src/Handlers/Border/BorderHandler.cs, src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs, src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs |
Original PR |
🚦 Gate — Test Verification
Gate Result: PASSED
Platform: windows
Mode: Full Verification
- Tests FAIL without fix:
- Tests PASS with fix:
Evidence: NestedBordersShouldNotCauseLayoutCycle failed without the fix because the app crashed during layout, and passed with the fix when the page loaded successfully.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix | Skip redundant Windows Path.Data update from ContentPanel.UpdateBorder when invoked during arrange, letting ContentPanelSizeChanged handle the post-arrange path update |
PASS | src/Core/src/Platform/Windows/ContentPanel.cs |
Works, but introduces one-shot flagging and deferred cleanup logic in Windows platform code |
| 2 | try-fix | Cache the last Windows shape/size update in ContentPanel and deduplicate Path.Data writes, pre-populating the cache in ArrangeOverride so arrange-triggered UpdateBorder becomes a no-op |
PASS | src/Core/src/Platform/Windows/ContentPanel.cs |
Works, but adds stateful cache coordination between arrange and size-changed paths |
| 3 | try-fix | Defer Windows Path.Data / RenderTransform writes from BorderExtensions.UpdatePath via DispatcherQueue.TryEnqueue so mutation happens after layout |
FAIL | src/Core/src/Platform/Windows/BorderExtensions.cs |
Never reached runtime validation due nullability compile errors (CS8602) |
| 4 | try-fix | In BorderHandler.PlatformArrange, on Windows skip UpdateValue(Shape) only when the ContentPanel is already at the arranged size, treating size-match as proof the platform layer already updated shape |
PASS | src/Core/src/Handlers/Border/BorderHandler.cs |
Works, but adds Windows-specific runtime heuristics to shared handler code |
| 5 | try-fix | Move all Windows geometry writes to ContentPanelSizeChanged and cache normalized geometry plus ScaleTransform, so arrange-time UpdateBorder never writes Path.Data |
PASS | src/Core/src/Platform/Windows/ContentPanel.cs |
Works, but is the most complex platform-layer redesign of the passing candidates |
| 6 | try-fix | Replace SizeChanged-driven geometry writes with a LayoutUpdated hook so Path.Data is applied only after layout stabilizes |
PASS | src/Core/src/Platform/Windows/ContentPanel.cs |
Works, but shifts border rendering to a post-layout event-driven model |
| PR | PR #34337 | Keep PlatformArrange override but skip Windows arrange-time UpdateValue(Shape) because ContentPanel.ContentPanelSizeChanged already handles size-based shape updates |
PASSED (Gate) | src/Core/src/Handlers/Border/BorderHandler.cs, src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs, src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs |
Simplest passing fix; matches existing architecture and is directly validated by Gate |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | N/A | Attempted Windows platform-layer guard in ContentPanel and passed |
| claude-sonnet-4.6 | 1 | N/A | Attempted cache-based Path.Data deduplication in ContentPanel and passed |
| gpt-5.3-codex | 1 | N/A | Attempted deferred BorderExtensions.UpdatePath; failed at compile time |
| gemini-3-pro-preview | 1 | N/A | Attempted runtime size-match guard in BorderHandler.PlatformArrange and passed |
| claude-opus-4.6 | 2 | Yes | NEW IDEA: cache normalized geometry per shape and scale the Path instead of regenerating geometry each size change |
| claude-sonnet-4.6 | 2 | Yes | NEW IDEA: use LayoutUpdated instead of SizeChanged/arrange timing to update the shape after the tree stabilizes |
| gpt-5.3-codex | 2 | No | NO NEW IDEAS |
| gemini-3-pro-preview | 2 | No | NO NEW IDEAS |
| claude-opus-4.6 | 3 | Yes | NEW IDEA: mutate a single PathGeometry in place rather than reassigning Path.Data |
| claude-sonnet-4.6 | 3 | Yes | NEW IDEA: isolate the border Path inside a Canvas so child invalidations do not bubble the same way |
| gpt-5.3-codex | 3 | No | NO NEW IDEAS |
| gemini-3-pro-preview | 3 | Yes | NEW IDEA: assign Path.Data once and update geometry figures/segments in place thereafter |
Exhausted: No reached the 3-round cross-pollination cap with a few more invasive, untested ideas remaining.
Best Fix Comparison
| Candidate | Passes test | Scope | Complexity | Servicing fit | Notes |
|---|---|---|---|---|---|
| PR #34337 | Yes | 1 handler file (+tests in PR) | Low | High | Directly addresses the redundant Windows arrange update and leaves Windows platform code unchanged |
| Attempt 1 | Yes | 1 Windows platform file | Medium | Medium | Works, but adds timing flag logic |
| Attempt 2 | Yes | 1 Windows platform file | Medium | Medium | Works, but adds cache/state coupling |
| Attempt 4 | Yes | 1 handler file | Medium | Medium | Works, but relies on runtime size heuristics |
| Attempt 5 | Yes | 1 Windows platform file | High | Low | Considerably more complex than needed |
| Attempt 6 | Yes | 1 Windows platform file | High | Low | Event-driven post-layout model is heavier than necessary |
Selected Fix: PR #34337 It is the simplest empirically validated solution, directly matches the diagnosed root cause on Windows, avoids new platform-layer state/event machinery, and is the best servicing-fit among all passing candidates.
📋 Report — Final Recommendation
Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | COMPLETE | Windows-only regression in Border layout; PR changes 1 implementation file and a matching HostApp/Appium UI-test pair |
| Gate | PASSED | Issue32406 failed without the fix and passed with the fix on windows |
| Try-Fix | COMPLETE | 6 attempts run, 5 passing; PR fix remained the best servicing-fit after comparison |
| Report | COMPLETE |
Summary
PR #34337 correctly fixes a Windows-only LayoutCycleException caused by nested Border elements. The added UI test reproduces the crash without the fix and passes with it. I also ran the mandatory multi-model try-fix phase: several alternative fixes passed, but all of them were more complex, more stateful, or more invasive than the PR's direct handler-level suppression of the redundant Windows arrange-time shape update.
Root Cause
On Windows, BorderHandler.PlatformArrange could call UpdateValue(nameof(IBorderStroke.Shape)) during the WinUI arrange pass. That flows into the Windows ContentPanel / BorderExtensions.UpdatePath path, which assigns Path.Data. Writing Path.Data during arrange invalidates layout and, with hundreds of nested borders, triggers a LayoutCycleException.
Fix Quality
The PR addresses the root cause directly by skipping the redundant Windows arrange-time shape update while preserving existing Windows size-based updates through ContentPanel.ContentPanelSizeChanged. Compared with the passing alternatives I tested, this is the simplest and clearest servicing fix: one small behavior change in the handler, no added platform-layer flags/caches/events, and no runtime heuristics beyond the platform conditional already justified by the Windows-specific rendering path.
📋 Expand PR Finalization Review
PR #34337 Finalization Review
Title: Good, optional improvement
Current: Fix LayoutCycleException from nested Borders on Windows
Assessment: Accurate and searchable.
Recommended (optional): [Windows] Border: Fix LayoutCycleException from nested Borders
Adding the platform/component prefix would better match the repository's commit-title style, but the current title is still acceptable.
Description: Strong, but needs one accuracy fix
Quality assessment:
- Structure: Clear root-cause and fix explanation
- Technical depth: Explains why the WinUI layout cycle happens
- Accuracy: Mostly accurate, but one stale bullet remains
- Completeness: Includes the required NOTE block and links the issue
Mismatch with final diff:
- The
### Changessection still says the PR updatessrc/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt. - That is no longer true in the final PR. The PublicAPI change was reverted when the author kept
PlatformArrangeon all TFMs and only conditionalized the Windows-specific body.
Recommended description edits:
- Remove the stale
PublicAPI.Unshipped.txtbullet. - Update the
BorderHandler.csbullet to reflect the final implementation:- Keep
PlatformArrangeon all TFMs. - Wrap only
_lastSizeand theUpdateValue(nameof(IBorderStroke.Shape))path in#if !WINDOWS.
- Keep
Optional cleanup:
- Rename
## Descriptionto### Description of Change. - Move
Fixes #32406under a### Issues Fixedheading to align with the PR template more closely.
Code Review Findings
Critical Issues
- None.
Suggestions / Follow-up Before Merge
Potential Windows regression: stroke-thickness-only updates can leave stale path geometry
- File:
src/Core/src/Handlers/Border/BorderHandler.cs - Problem: This PR intentionally stops recalculating border geometry during
PlatformArrangeon Windows and relies onContentPanel.ContentPanelSizeChangedinstead. That fixes the layout cycle, butStrokeThicknessalso affects the computed path geometry on Windows (BorderExtensions.UpdatePathsubtractsStrokeThicknessfrom width/height and updates the render transform).MapStrokeThicknesscurrently updates onlyPath.StrokeThickness; it does not recalculatePath.Data, and changing stroke thickness alone does not triggerContentPanelSizeChanged. - Impact: A border whose
StrokeThicknesschanges after initial layout can render with stale geometry on Windows. - Recommendation: Before merge, handle the Windows
StrokeThicknesspath by forcing a geometry refresh when thickness changes without a size change, and consider adding coverage for that scenario.
Looks Good
- The core fix is well-targeted and matches the existing Windows
ContentPanel.ContentPanelSizeChangedbehavior for size-driven updates. - The final implementation avoids unnecessary public API churn by keeping
PlatformArrangepresent on all TFMs. - The UI test was updated to use
WaitForTextToBePresentInElement, which avoids the original race between page load and text assertion. - PR checks are green (
gh pr checks 34337reported all checks successful, including UI/device test lanes), which increases confidence that the added repro test is stable.
Overall
Recommendation: Do not finalize as-is. First, correct the stale PR-description bullet and resolve the Windows StrokeThickness geometry regression risk described above.
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could <a href="https://github.com/dotnet/maui/wiki/Testing-PR-Builds">test the resulting artifacts</a> from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Fixes #32406 On Windows, deeply nested `Border` elements (300+ at 3+ nesting depth) in ControlTemplates cause an unrecoverable `LayoutCycleException`. This is a regression from PR #24844 which moved Border shape updates from `OnPropertyChanged` (Width/Height) into `BorderHandler.PlatformArrange`. ### Root Cause The layout cycle is caused by **double shape-geometry updates during the WinUI arrange pass**: 1. `ContentPanel.ContentPanelSizeChanged` (pre-existing) fires during WinUI's arrange pass and sets `Path.Data` 2. `BorderHandler.PlatformArrange` (added by #24844) also calls `UpdateValue(Shape)` during arrange, which sets `Path.Data` again Setting `Path.Data` on a WinUI `Path` element invalidates layout (triggers re-measure/arrange). With two invalidations per Border per arrange pass, and hundreds of deeply nested Borders, the cascading invalidations exceed WinUI's layout cycle detection threshold. ### Fix Skip the `PlatformArrange` shape update on Windows (`#if !WINDOWS`), since `ContentPanel.ContentPanelSizeChanged` already handles size-based shape updates during the layout pass. Non-Windows platforms retain `PlatformArrange` behavior since they have no equivalent `SizeChanged` handler. ### Changes - `src/Core/src/Handlers/Border/BorderHandler.cs` — Wrap `PlatformArrange` override and `_lastSize` field with `#if !WINDOWS` - `src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt` — Mark removed API - `src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs` — HostApp page with 350 nested Borders at depth 3 - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs` — UI test verifying no crash --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
#34575) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Adds Windows platform support to the `maui-copilot` CI pipeline (AzDO definition 27723), enabling Copilot PR reviews on Windows-targeted PRs. ### Changes **`eng/pipelines/ci-copilot.yml`** - Add `catalyst` and `windows` to Platform parameter values - Add per-platform pool selection (`androidPool`, `iosPool`, `macPool`, `windowsPool`) - Skip Xcode, Android SDK, simulator setup for Windows/Catalyst - Add Windows-specific "Set screen resolution" step (1920x1080) - Add MacCatalyst-specific "Disable Notification Center" step - Fix `sed` command for `Directory.Build.Override.props` on Windows (Git Bash uses GNU sed) - Handle Copilot CLI PATH detection on Windows vs Unix - Change `script:` steps to `bash:` for cross-platform consistency **`.github/scripts/Review-PR.ps1`** - Add `catalyst` to ValidateSet for Platform parameter **`.github/scripts/BuildAndRunHostApp.ps1`** - Add Windows test assembly directory for artifact collection **`.github/scripts/post-ai-summary-comment.ps1` / `post-pr-finalize-comment.ps1`** - Various improvements for cross-platform comment posting ### Validation Successfully ran the pipeline with `Platform=windows` on multiple Windows-specific PRs: - PR #27713 — ✅ Succeeded - PR #34337 — ✅ Succeeded - PR #26217, #27609, #27880, #28617, #29927, #30068 — Triggered and running --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could <a href="https://github.com/dotnet/maui/wiki/Testing-PR-Builds">test the resulting artifacts</a> from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Fixes #32406 On Windows, deeply nested `Border` elements (300+ at 3+ nesting depth) in ControlTemplates cause an unrecoverable `LayoutCycleException`. This is a regression from PR #24844 which moved Border shape updates from `OnPropertyChanged` (Width/Height) into `BorderHandler.PlatformArrange`. ### Root Cause The layout cycle is caused by **double shape-geometry updates during the WinUI arrange pass**: 1. `ContentPanel.ContentPanelSizeChanged` (pre-existing) fires during WinUI's arrange pass and sets `Path.Data` 2. `BorderHandler.PlatformArrange` (added by #24844) also calls `UpdateValue(Shape)` during arrange, which sets `Path.Data` again Setting `Path.Data` on a WinUI `Path` element invalidates layout (triggers re-measure/arrange). With two invalidations per Border per arrange pass, and hundreds of deeply nested Borders, the cascading invalidations exceed WinUI's layout cycle detection threshold. ### Fix Skip the `PlatformArrange` shape update on Windows (`#if !WINDOWS`), since `ContentPanel.ContentPanelSizeChanged` already handles size-based shape updates during the layout pass. Non-Windows platforms retain `PlatformArrange` behavior since they have no equivalent `SizeChanged` handler. ### Changes - `src/Core/src/Handlers/Border/BorderHandler.cs` — Wrap `PlatformArrange` override and `_lastSize` field with `#if !WINDOWS` - `src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt` — Mark removed API - `src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs` — HostApp page with 350 nested Borders at depth 3 - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs` — UI test verifying no crash --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
> [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could <a href="https://github.com/dotnet/maui/wiki/Testing-PR-Builds">test the resulting artifacts</a> from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Fixes dotnet#32406 On Windows, deeply nested `Border` elements (300+ at 3+ nesting depth) in ControlTemplates cause an unrecoverable `LayoutCycleException`. This is a regression from PR dotnet#24844 which moved Border shape updates from `OnPropertyChanged` (Width/Height) into `BorderHandler.PlatformArrange`. ### Root Cause The layout cycle is caused by **double shape-geometry updates during the WinUI arrange pass**: 1. `ContentPanel.ContentPanelSizeChanged` (pre-existing) fires during WinUI's arrange pass and sets `Path.Data` 2. `BorderHandler.PlatformArrange` (added by dotnet#24844) also calls `UpdateValue(Shape)` during arrange, which sets `Path.Data` again Setting `Path.Data` on a WinUI `Path` element invalidates layout (triggers re-measure/arrange). With two invalidations per Border per arrange pass, and hundreds of deeply nested Borders, the cascading invalidations exceed WinUI's layout cycle detection threshold. ### Fix Skip the `PlatformArrange` shape update on Windows (`#if !WINDOWS`), since `ContentPanel.ContentPanelSizeChanged` already handles size-based shape updates during the layout pass. Non-Windows platforms retain `PlatformArrange` behavior since they have no equivalent `SizeChanged` handler. ### Changes - `src/Core/src/Handlers/Border/BorderHandler.cs` — Wrap `PlatformArrange` override and `_lastSize` field with `#if !WINDOWS` - `src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt` — Mark removed API - `src/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs` — HostApp page with 350 nested Borders at depth 3 - `src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs` — UI test verifying no crash --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description
Fixes #32406
On Windows, deeply nested
Borderelements (300+ at 3+ nesting depth) in ControlTemplates cause an unrecoverableLayoutCycleException. This is a regression from PR #24844 which moved Border shape updates fromOnPropertyChanged(Width/Height) intoBorderHandler.PlatformArrange.Root Cause
The layout cycle is caused by double shape-geometry updates during the WinUI arrange pass:
ContentPanel.ContentPanelSizeChanged(pre-existing) fires during WinUI's arrange pass and setsPath.DataBorderHandler.PlatformArrange(added by Speed-up Border rendering by avoiding useless pass during size allocation #24844) also callsUpdateValue(Shape)during arrange, which setsPath.DataagainSetting
Path.Dataon a WinUIPathelement invalidates layout (triggers re-measure/arrange). With two invalidations per Border per arrange pass, and hundreds of deeply nested Borders, the cascading invalidations exceed WinUI's layout cycle detection threshold.Fix
Skip the
PlatformArrangeshape update on Windows (#if !WINDOWS), sinceContentPanel.ContentPanelSizeChangedalready handles size-based shape updates during the layout pass. Non-Windows platforms retainPlatformArrangebehavior since they have no equivalentSizeChangedhandler.Changes
src/Core/src/Handlers/Border/BorderHandler.cs— WrapPlatformArrangeoverride and_lastSizefield with#if !WINDOWSsrc/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt— Mark removed APIsrc/Controls/tests/TestCases.HostApp/Issues/Issue32406.cs— HostApp page with 350 nested Borders at depth 3src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32406.cs— UI test verifying no crash