[Windows] TabbedPage: Refresh layout when NavigationView size changes#26217
[Windows] TabbedPage: Refresh layout when NavigationView size changes#26217BagavathiPerumal wants to merge 5 commits intodotnet:mainfrom
Conversation
|
/azp run |
This comment was marked as outdated.
This comment was marked as outdated.
|
@PureWeen / @jsuarezruiz, We have analysed the test case (Issue1323Test) failure and found that the issue was caused by a button alignment-related issue. Previously, the button inside the page, which was added as a child within the TabbedPage, was not properly aligned in the center. In this test case, the button was added directly as the content of the page without any additional properties. However, when the page was added as a child page within the TabbedPage, the button was not properly centered. After the fix, the button inside the page, which was added as a child in the TabbedPage, was properly aligned in the center of the page. The fix involved calling this.InvalidateMeasure(), which triggered a layout recalculation. This allowed the element within the TabbedPage to be measured and arranged correctly during the subsequent layout cycle. As a result, the button was properly aligned in the center of the page. Previously, the layout measurements were not recalculated, which caused the button to be misaligned on the page. Based on the current behavior after the fix, can we resave the current snapshot as the expected snapshot for this test case (Issue1323Test). Button Alignment in the Page (Output image from the simple sample): Page added as Child in the TabbedPge(Output image from the simple sample): Testcase Image (Before Fix): Current Testcase Image (After Fix): |
| { | ||
| if (_navigationView != null) | ||
| { | ||
| this.InvalidateMeasure(); |
There was a problem hiding this comment.
If you remove the call to this.Arrange and leave this.InvalidateMeasure does this all still work?
The call to arrange inside the SizeChanged method here doesn't seem correct.
Also, can you add a test?
There was a problem hiding this comment.
@PureWeen, I have implemented the suggested changes, tested the fix across all basic scenarios, and also ran UI tests.
Testcase: The test case cannot be created for this scenario as resizing the window to a minimized state is not feasible during testing. Therefore, we have not added a test case for this scenario. Could you suggest alternative ways to address this, since resizing the window to a minimized state isn't possible during testing.
There was a problem hiding this comment.
Do you need to resize the Window or minimize and maximize it?
There was a problem hiding this comment.
@jsuarezruiz, The issue occurs only when resizing the window. Could you please share your suggestions on how to resolve it.
There was a problem hiding this comment.
@PureWeen, The Arrange() call in the NavigationView SizeChanged method is necessary to prevent test failures. Without this change, the ChangingToNewMauiContextDoesntCrash test consistently fails with a timeout exception.
|
/azp run |
This comment was marked as off-topic.
This comment was marked as off-topic.
1 similar comment
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
@BagavathiPerumal I've tried your pr with my app and it does appear to fix the issue, thanks! |
@Mark-NC001, Thank you so much for taking the time to test the PR with your app. We're truly glad to hear that it resolves the issue. |
| { | ||
| if (_navigationView != null) | ||
| { | ||
| this.InvalidateMeasure(); |
There was a problem hiding this comment.
Do you need to resize the Window or minimize and maximize it?
@jsuarezruiz, It looks expected behavior, as we already mentioned here, can we resave the current snapshot as the expected snapshot for this test case (Issue1323Test). |
|
/rebase |
dd46dc2 to
ea5714d
Compare
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
jsuarezruiz
left a comment
There was a problem hiding this comment.
Could be possible to include a test case for this changes?
In Appium, can minimize and maximize the Window (even change the Window size) using:
app.Driver.Manage().Window.Maximize();
app.Driver.Manage().Window.Maximize();
Let me know if can help in someway.
@jsuarezruiz, As suggested, I have tried recreating the test case using Appium but was unable to do so for this scenario. A NotImplementedException occurs when attempting to set the size value using testApp.Driver.Manage().Window.Size. Exception details: System.NotImplementedException: Unhandled endpoint: /session/2EDC486C-4A59-4E60-8C80-3EC866B72868/window/rect -- http://127.0.0.1:10100/ with parameters { Code: I also tried creating a test case based on a button click to change the window size, but this approach did not reproduce the issue. The issue only occurs when resizing the window by dragging. Could you please share your suggestions on how to resolve it. |
There was a problem hiding this comment.
There are some TabbedPage Windows tests failing.
Issue1323Test
Snapshot different than baseline: Issue1323Test.png (0.90% difference)

(in red, the differences)
TestPageDoesntCrash
at UITest.Appium.HelperExtensions.Tap(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 302
at UITest.Appium.HelperExtensions.Tap(IApp app, IQuery query) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 38
at UITest.Appium.HelperExtensions.TapMoreButton(IApp app) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2261
at Microsoft.Maui.TestCases.Tests.Issues.Issue2809.TestPageDoesntCrash() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue2809.cs:line 19
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Could you review if are related with the changes?
|
/rebase |
ea5714d to
dd0c091
Compare
3f7422d to
acd0b97
Compare
78eebbc to
fc7ca15
Compare
…to fix scrolling and layout issues.
…ing InvalidateMeasure() and Snapshot resaved.
fc7ca15 to
e4a80c4
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 26217Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 26217" |
I’ve reviewed and updated the changes based on the latest findings. Specifically:
|
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #26217 | Add InvalidateMeasure() before Arrange(_navigationView) in OnNavigationViewSizeChanged |
⏳ PENDING | TabbedPage.Windows.cs |
Reviewer questions whether Arrange() is needed |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | InvalidateMeasure() only remove Arrange() entirely |
PASS | TabbedPage.Windows.cs |
Simplest; aligns with reviewer's suggestion |
| 2 | try-fix (claude-sonnet-4.6) | Dispatcher.Dispatch(() => InvalidateMeasure()) deferred |
PASS | TabbedPage.Windows.cs |
Avoids re-entrancy but unnecessary complexity |
| 3 | try-fix (gpt-5.3-codex) | LayoutUpdated event + cache dimensions + CurrentPage?.InvalidateMeasure() + Arrange() |
PASS | TabbedPage.Windows.cs |
Over-engineered (+24 lines) |
| 4 | try-fix (gpt-5.4) | foreach (var child in Children) child.InvalidateMeasure() |
PASS | TabbedPage.Windows.cs |
Less correct parent should invalidate, not children |
| PR | PR #26217 | InvalidateMeasure() + Arrange(_navigationView) |
PASSED (Gate skipped) | TabbedPage.Windows.cs |
Reviewer questioned Arrange() in SizeChanged |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | NO NEW IDEAS | Confirmed Attempt 1 is cleanest; PR's Arrange() call is redundant |
Exhausted: Yes
Selected Fix: Candidate #1 (Attempt 1) InvalidateMeasure() only. Reason: Simplest fix (1-line change), directly matches reviewer PureWeen's suggestion, removes the questionable Arrange() call from a SizeChanged handler. InvalidateMeasure() triggers a full measurearrange cycle, making the explicit Arrange() call redundant.
📋 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issues #26103, #11402, #20028; Windows TabbedPage layout on resize |
| Gate | No tests detected in PR | |
| Try-Fix | ✅ COMPLETE | 4 attempts, 4 passing — better alternative found |
| Report | ✅ COMPLETE |
Summary
The PR fixes a real, long-standing Windows bug: TabbedPage content (especially ScrollViews) clips incorrectly after window resize. The fix is in OnNavigationViewSizeChanged and the core idea of calling InvalidateMeasure() is correct. However, the PR keeps the pre-existing this.Arrange(_navigationView) call alongside InvalidateMeasure(), which the reviewer (PureWeen) specifically questioned. Agent exploration found that InvalidateMeasure() alone is sufficient and simpler.
Root Cause
When NavigationView fires SizeChanged, MAUI's layout system holds stale measurements. The original code called this.Arrange(_navigationView) (forces layout with ActualWidth/ActualHeight), but skipped InvalidateMeasure(), so MAUI re-arranged using cached (pre-resize) constraint sizes — causing clipping.
Fix Quality
The PR's fix works but is suboptimal:
- ✅
InvalidateMeasure()is the correct signal — it marks layout as dirty and schedules a proper measure→arrange cycle ⚠️ The retainedthis.Arrange(_navigationView)call is redundant and architecturally questionable — callingIView.Arrange()synchronously inside aSizeChangedcallback is unusual and could cause re-entrancy in edge cases
Better alternative (Attempt #1): Remove this.Arrange(_navigationView) and keep only this.InvalidateMeasure():
void OnNavigationViewSizeChanged(object sender, SizeChangedEventArgs e)
{
if (_navigationView != null)
this.InvalidateMeasure();
}This is a 1-line change, passes the same tests, and directly implements what reviewer PureWeen suggested. It lets MAUI's layout system handle the full measure→arrange pass naturally.
Concerns
- No new tests — The author acknowledges window-resize scenarios can't be automated. However, the resaved snapshots suggest layout did change. Reviewer should confirm the 6 resaved snapshots are intentional improvements and not regressions.
ChangingToNewMauiContextDoesntCrashtimeout claim — Author says removingArrange()causes this device test to timeout. This was NOT reproduced in try-fix (attempt 1 passed withoutArrange()). The agent ran a UI test (Issue28838), not the device test. If the device test truly fails withoutArrange(), it may indicate a deeper lifecycle issue that theArrange()call is papering over.- Snapshot changes — 6 Windows test snapshots were resaved. These should be reviewed carefully to confirm the layout changes are correct improvements and not unintended side-effects.
Recommendation
Request changes to:
- Remove the
this.Arrange(_navigationView)call per reviewer suggestion —InvalidateMeasure()alone is sufficient - Confirm the
ChangingToNewMauiContextDoesntCrashdevice test still passes withoutArrange()(the agent's try-fix suggests it should) - Review the 6 resaved snapshots to confirm they represent correct layout (not regressions)
kubaflo
left a comment
There was a problem hiding this comment.
Please review the ai's summary :)
I have reviewed the AI summary. The Arrange() call in the NavigationView.SizeChanged method is necessary to prevent test failures. Without this change, the ChangingToNewMauiContextDoesntCrash test consistently fails with a TimeoutException. |
#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>
🚦 Gate - Test Before and After Fix📊 Expand Full Gate —
|







Root cause
The issue arises because the OnNavigationViewSizeChanged method fails to properly reset the layout measurements before arranging the NavigationView. As a result, the NavigationView does not correctly update its layout in response to size changes, causing misalignment or rendering issues in the ScrollView.
Description of Issue Fix
The fix involves updating the OnNavigationViewSizeChanged() method to include a call to InvalidateMeasure() before arranging the NavigationView. This ensures that the layout is accurately recalculated, allowing the ScrollView and other elements within the TabbedPage to be properly measured and arranged during the subsequent layout cycle. This effectively resolves alignment and rendering issues.
Additionally, the Arrange() call is retained within the SizeChanged handler to prevent test failures, specifically avoiding timeout issues observed in the ChangingToNewMauiContextDoesntCrash test. This combination ensures stable layout behavior while resolving the clipping and scrolling issues that occur after window resizing.
Why Tests were not added:
Regarding the test case: The issue only occurs when resizing the window, so it is not possible to add a test case for the window resizing behavior.
Tested the behavior in the following platforms.
Issues Fixed
Fixes #26103
Fixes #11402
Fixes #20028
Resaved Test snapshots
Resaved the below-mentioned test snapshot because elements in the TabbedPage were not properly aligned before the fix. The layout changes in OnNavigationViewSizeChanged (adding Arrange() after InvalidateMeasure()) now ensure proper element alignment within the TabbedPage.
Output
BeforeFix-TabbedPage-ScrollView.mp4
AfterFix-TabbedPage-ScrollView.mp4