Skip to content

[Windows] TabbedPage: Refresh layout when NavigationView size changes#26217

Open
BagavathiPerumal wants to merge 6 commits intodotnet:mainfrom
BagavathiPerumal:fix-26103
Open

[Windows] TabbedPage: Refresh layout when NavigationView size changes#26217
BagavathiPerumal wants to merge 6 commits intodotnet:mainfrom
BagavathiPerumal:fix-26103

Conversation

@BagavathiPerumal
Copy link
Copy Markdown
Contributor

@BagavathiPerumal BagavathiPerumal commented Nov 29, 2024

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.

  • Windows
  • Mac
  • iOS
  • Android

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.

  1. DefaultSelectedTabTextColorShouldApplyProperly
  2. FontImageSourceColorShouldApplyOnTabIcon
  3. VerifyTabbedPageMenuItemTextColor
  4. DynamicFontImageSourceColorShouldApplyOnTabIcon
  5. Issue1323Test
  6. TabBarIconsShouldAutoscaleTabbedPage

Output

Before Issue Fix After Issue Fix
BeforeFix-TabbedPage-ScrollView.mp4
AfterFix-TabbedPage-ScrollView.mp4

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Nov 29, 2024
@jsuarezruiz
Copy link
Copy Markdown
Contributor

jsuarezruiz commented Nov 29, 2024

/azp run

@azure-pipelines

This comment was marked as outdated.

@BagavathiPerumal BagavathiPerumal marked this pull request as ready for review November 29, 2024 11:40
@BagavathiPerumal BagavathiPerumal requested a review from a team as a code owner November 29, 2024 11:40
@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

@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):

image

Page added as Child in the TabbedPge(Output image from the simple sample):

image

Testcase Image (Before Fix):

image

Current Testcase Image (After Fix):

image

@sheiksyedm sheiksyedm added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Dec 6, 2024
{
if (_navigationView != null)
{
this.InvalidateMeasure();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

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.

Do you need to resize the Window or minimize and maximize it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@jsuarezruiz, The issue occurs only when resizing the window. Could you please share your suggestions on how to resolve it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

This comment was marked as off-topic.

1 similar comment
@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@Mark-NC001
Copy link
Copy Markdown

@BagavathiPerumal I've tried your pr with my app and it does appear to fix the issue, thanks!

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

@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.

@jfversluis jfversluis requested a review from PureWeen January 24, 2025 19:27
Copy link
Copy Markdown
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

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

The test Issue1323Test related with TabbedPage and Navigation is failing on Windows. Small snapshot differences.
image

{
if (_navigationView != null)
{
this.InvalidateMeasure();
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.

Do you need to resize the Window or minimize and maximize it?

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

The test Issue1323Test related with TabbedPage and Navigation is failing on Windows. Small snapshot differences. image

@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).

#26217 (comment)

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/rebase

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Copy Markdown
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

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

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.

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

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 {
wildcards = (
"session/2EDC486C-4A59-4E60-8C80-3EC866B72868/window/rect"
);
}
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary2 parameters) at OpenQA.Selenium.WebDriver.InternalExecute(String driverCommandToExecute, Dictionary2 parameters)
at OpenQA.Selenium.Window.set_Size(Size value)
at Microsoft.Maui.TestCases.Tests.Issues.Issue26103.VerifyTabbedPageScrollingBehavior() in /Users/bagavathi/Downloads/maui-fix-26103/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue26103.cs:line 25
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Code:

using Microsoft.Maui.Controls.PlatformConfiguration;
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
    public class Issue26103 : _IssuesUITest
    {
        public Issue26103(TestDevice testDevice) : base(testDevice)
        {
        }
        public override string Issue => "TabbedPage - ScrollView not allowing scrolling when it should";
        [Test]
        [Category(UITestCategories.TabbedPage)]
        public void VerifyTabbedPageScrollingBehavior()
        {
            var testApp = (App as AppiumApp);
            if (testApp != null && testApp.Driver != null)
            {
                var originalWindowSize = testApp.Driver.Manage().Window.Size;
                testApp.Driver.Manage().Window.Size = new System.Drawing.Size(100, 200);
                testApp.WaitForElement("stackLayout1");
                VerifyScreenshot();
                testApp.Driver.Manage().Window.Size = originalWindowSize;
            }
        }
    }
}

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.

Copy link
Copy Markdown
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

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

There are some TabbedPage Windows tests failing.

Issue1323Test
Snapshot different than baseline: Issue1323Test.png (0.90% difference)
image
(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?

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

There are some TabbedPage Windows tests failing.

Issue1323Test Snapshot different than baseline: Issue1323Test.png (0.90% difference) image (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?

@jsuarezruiz,

Case1: Issue1323Test

It looks expected behavior, as we already mentioned here, can we resave the current snapshot as the expected snapshot for this test case (Issue1323Test).

#26217 (comment)

Case2: TestPageDoesntCrash

I have verified that the test case failures are unrelated to the fix. This has been confirmed on a local machine.

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/rebase

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 19, 2026

🚀 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 -- 26217

Or

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

@BagavathiPerumal BagavathiPerumal changed the title [Windows]Fix for TabbedPage - ScrollView not allowing scrolling when it should [Windows] TabbedPage: Refresh layout when NavigationView size changes Mar 19, 2026
@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

🤖 AI Summary

📊 Expand Full Reviewfc7ca15 · fix-26103-Resaved the test image.
🔍 Pre-Flight — Context & Validation
Issue: #26103 - TabbedPage - ScrollView not allowing scrolling when it should Related Issues: #11402 - TabbedPage App on resize hides page bottom content; #20028 - Grid overflows child ContentPage of parent TabbedPage on initial load and when resizing on Windows PR: #26217 - [Windows]Fix for TabbedPage - ScrollView not allowing scrolling when it should Platforms Affected: Windows Files Changed: 1 implementation, 6 test snapshot baselines

Key Findings

  • All three linked issues describe the same Windows-only TabbedPage resize/layout problem: child page content shifts or overflows after window resize, causing lost bottom content or incorrect ScrollView extent.
  • The PR changes src\Controls\src\Core\TabbedPage\TabbedPage.Windows.cs and resaves six WinUI snapshot baselines, but it does not add a new executable regression test for the resize behavior reviewers requested.
  • Reviewer feedback specifically questioned whether Arrange() is necessary in the size-changed handler and repeatedly asked for a test that proves the fix.
  • PR discussion indicates Issue1323Test snapshot drift was treated as expected fallout from the layout change, while another failing test (Issue2809) was reported as unrelated.
  • A user comment on the PR confirms the branch appears to fix their real app, but pre-flight does not validate that claim.

Edge Cases / Discussion Notes

  • The issue is triggered by window resizing on Windows; one report notes width changes can toggle whether the ScrollView reaches the bottom.
  • Another related issue reports the broken layout can be present on initial load and then behave inconsistently as the window shrinks.
  • The author reports Appium window resizing hit a NotImplementedException, which explains why the PR currently lacks a direct UI regression test.

Fix Candidates

Source Approach Test Result Files Changed Notes

PR PR #26217 In OnNavigationViewSizeChanged, call InvalidateMeasure() and then Arrange(_navigationView) so TabbedPage remeasures/rearranges after NavigationView size changes; resave affected WinUI snapshots PENDING (Gate) src\\Controls\\src\\Core\\TabbedPage\\TabbedPage.Windows.cs, snapshot baselines Original PR; reviewers requested proof that Arrange() is required and asked for a real test
🚦 Gate — Test Verification

Gate Result: SKIPPED

Platform: Windows Mode: Full Verification

  • Tests in PR (TestCases.HostApp / TestCases.Shared.Tests): None added
  • Tests FAIL without fix: N/A
  • Tests PASS with fix: N/A

Notes:

  • The PR modifies one implementation file and six Windows snapshot baselines, but it does not add a HostApp page or Shared UI test for the resize/scroll regression.
  • Because no PR-added executable regression test exists, the required fail-without-fix / pass-with-fix verification could not be performed.
  • Reviewer feedback on the PR explicitly requested a test, and the author noted Appium window resizing hit NotImplementedException, which appears to be why coverage is still missing.

🔧 Fix — Analysis & Comparison

Fix Candidates

Source Approach Test Result Files Changed Notes

1 try-fix (claude-opus-4.6) Invalidate _displayedPage on NavigationView size change and rely on the normal layout cycle instead of explicitly calling Arrange(_navigationView) PASS 1 file More targeted than the PR; directly addresses reviewer concern about Arrange()
2 try-fix (claude-sonnet-4.6) Call _navigationFrame?.UpdateLayout() on NavigationView size change to force a synchronous WinUI layout pass of the tab content subtree PASS 1 file Platform-level alternative; also removes the explicit Arrange() call
3 try-fix (gpt-5.3-codex) Clear cached _displayedPage and call UpdateCurrentPageContent() on NavigationView size change to refresh and remeasure the active tab content PASS 3 files during attempt Passing alternative, but it touched test files during the attempt to validate a new scenario
4 try-fix (gemini-3-pro-preview) Call _navigationFrame?.InvalidateMeasure() on NavigationView size change instead of invalidating the TabbedPage or arranging the NavigationView PASS 1 file Targeted content-frame invalidation also passes the proxy test
5 try-fix (claude-opus-4.6) Defer into the dispatcher and invalidate the nested ContentPresenter (InvalidateMeasure + InvalidateArrange) after NavigationView finishes resizing PASS 1 file Post-layout subtree invalidation also succeeds under the same proxy test
6 try-fix (claude-sonnet-4.6) Call _navigationView?.InvalidateArrange() on size change so native WinUI re-arranges the container and its children PASS 1 file Cleanest single-call WinUI-native alternative among the passing candidates
7 try-fix (gemini-3-pro-preview) Hook _navigationFrame.SizeChanged and react there instead of _navigationView.SizeChanged FAIL 1 file Failed the Windows proxy test with snapshot difference, suggesting the frame hook misses the real trigger
8 try-fix (claude-opus-4.6) Wrap the frame in a custom Grid host that invalidates the displayed page during arrange-size changes FAIL 1 file More invasive visual-tree rewrite caused a 0.89% snapshot difference
9 try-fix (gemini-3-pro-preview) Set _navigationView.HorizontalContentAlignment and .VerticalContentAlignment to Stretch and remove the size-change nudges PASS 1 file Declarative alternative, but root-cause confidence is lower than candidate 6
PR PR #26217 Invalidate the TabbedPage and explicitly call Arrange(_navigationView) in OnNavigationViewSizeChanged; resave affected WinUI snapshots NOT GATED 1 implementation file + 6 snapshot baselines No dedicated regression test was added, so Gate could not verify fail-without-fix / pass-with-fix

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 Yes Invalidate/arrange the ContentPresenter child in a deferred post-layout callback.
claude-sonnet-4.6 1 Yes Call _navigationView?.InvalidateArrange() on size change.
gpt-5.3-codex 1 No NO NEW IDEAS
gemini-3-pro-preview 1 Yes Hook _navigationFrame size changes instead of _navigationView and react to the actual content area size.
claude-opus-4.6 2 Yes Replace manual size-change nudges with a custom Grid host that naturally stretches the NavigationView/Frame.
claude-sonnet-4.6 2 No NO NEW IDEAS
gpt-5.3-codex 2 No NO NEW IDEAS
gemini-3-pro-preview 2 Yes Force _navigationView content alignments to Stretch.
claude-opus-4.6 3 Yes Custom LayoutPanel host that explicitly measures/arranges current page on every size change.
claude-sonnet-4.6 3 No NO NEW IDEAS
gpt-5.3-codex 3 No NO NEW IDEAS
gemini-3-pro-preview 3 No NO NEW IDEAS
Exhausted: No reached the 3-round cross-pollination limit with one remaining architectural rewrite idea untried. Selected Fix: Candidate #6 (_navigationView?.InvalidateArrange()) simplest passing WinUI-native fix among the tried alternatives, though confidence is limited because the PR still lacks a dedicated fail-without-fix / pass-with-fix regression test.

📋 Report — Final Recommendation

Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight COMPLETE Windows-only TabbedPage resize/layout bug; PR changes 1 implementation file and 6 WinUI snapshot baselines.
Gate SKIPPED PR adds no dedicated TestCases.HostApp / TestCases.Shared.Tests regression test, so fail-without-fix / pass-with-fix verification could not be run.
Try-Fix COMPLETE 9 attempts run, 7 passing and 2 failing; multiple materially different alternatives passed the current Windows proxy test.
Report COMPLETE

Summary

The PR is addressing a real Windows TabbedPage resize bug, but it still should not be approved in its current form. The main blocker is test quality: there is no dedicated regression test for the resize/scroll scenario reviewers asked for, so the gate had to be skipped. In try-fix, seven materially different alternatives passed the available proxy test (Issue1323Test), which means the current validation is too weak to show that the PR's specific InvalidateMeasure() + Arrange() fix is necessary or best.

Root Cause

The underlying problem appears to be stale layout propagation after NavigationView size changes on Windows TabbedPage hosting. Child content (especially the frame/ScrollView subtree) is not consistently remeasured or rearranged when the container resizes, causing clipped bottom content or incorrect scroll extent after window resize.

Fix Quality

The PR fix is plausible, but it is heavier than necessary and still unproven. Reviewer feedback specifically questioned the explicit Arrange() call, and try-fix found cleaner passing alternatives such as candidate #6 (_navigationView?.InvalidateArrange()) and candidate #1 (_displayedPage?.InvalidateMeasure()). Because no dedicated regression test exists, I do not have enough evidence to recommend the PR's current implementation over those simpler options. I would request changes to add a real reproducible regression test for the resize behavior and then re-evaluate the implementation choice against that stronger test.

📋 Expand PR Finalization Review

I’ve reviewed and updated the changes based on the latest findings. Specifically:

  • Verified the AI-suggested fix on the Windows platform and confirmed that it does not resolve the issue
  • Confirmed that the suggested code changes do not address the root cause of the problem
  • Retained the existing implementation, as the AI-proposed modifications are not effective in resolving the issue
  • Checked the possibilities to add the testcase for the windows resizing behavior. However, unable to add the testcase for the windows resizing behavior.
  • Ensured the findings are aligned with the current PR status and reviewer expectations
  • The InvalidateMeasure() + immediate Arrange(...) pattern in OnNavigationViewSizeChanged() requires justification, as it is an uncommon layout approach and lacks test validation; however, Arrange() is currently necessary to prevent failures in ChangingToNewMauiContextDoesntCrash.
  • Updated the description and title based on the changes made in the PR.

@MauiBot MauiBot added the s/agent-gate-failed AI could not verify tests catch the bug label Mar 19, 2026
@dotnet dotnet deleted a comment from MauiBot Mar 19, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Mar 19, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

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

Please review the ai's summary :)

@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

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.

PureWeen pushed a commit that referenced this pull request Mar 23, 2026
#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>
@MauiBot MauiBot added s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-fix-win AI found a better alternative fix than the PR labels Apr 5, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 8, 2026

/rebase

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

🏷️ Test Categories for Regression Detection

No [Category] attributes were found in added test lines, but the changed source file (TabbedPage.Windows.cs) and updated Windows snapshot images indicate TabbedPage is the affected area. Categories were inferred from the source path.

UI Test Categories

Category Source
TabbedPage Inferred from src/Controls/src/Core/TabbedPage/TabbedPage.Windows.cs

Pipeline filter: TabbedPage

Device Test Categories

Category Project Source
TabbedPage Controls Inferred from src/Controls/src/Core/TabbedPage/TabbedPage.Windows.cs

Recommendation: Run device tests: Yes — Windows-specific TabbedPage platform handler code changed.

🚀 Run Targeted Tests on Existing Pipelines

Both maui-pr-uitests and maui-pr-devicetests now support category filtering parameters. To run only the relevant tests for this PR:

UI Tests — trigger maui-pr-uitests with:

Parameter: uiTestCategories = TabbedPage

Device Tests — trigger maui-pr-devicetests with:

Parameter: deviceTestCategories = TabbedPage

When triggered without parameters (e.g., by normal PR push), all categories run as usual.

📁 Changed files (7)

Test/snapshot files (6):

  • src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DefaultSelectedTabTextColorShouldApplyProperly.png
  • src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/DynamicFontImageSourceColorShouldApplyOnTabIcon.png
  • src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/FontImageSourceColorShouldApplyOnTabIcon.png
  • src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue1323Test.png
  • src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabBarIconsShouldAutoscaleTabbedPage.png
  • src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTabbedPageMenuItemTextColor.png

Source files (1):

  • src/Controls/src/Core/TabbedPage/TabbedPage.Windows.cs

ℹ️ Categories are detected from [Category()] attributes in the diff and inferred from changed source file paths.

🏷️ Category detection by Detect Test Categories for Regression Detection

@dotnet dotnet deleted a comment from MauiBot Apr 12, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 12, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 12, 2026

🚦 Gate — Test Before and After Fix

👋 @BagavathiPerumal — new gate results are available. Please review the latest session below.

🚦 Gate Session713f1e8 · fix-26108-Made code changes to bypass the extension method guard and Inline code comments have been removed to align with the repository style. · 2026-04-13 11:50 UTC

Gate Result: ⚠️ SKIPPED

No tests were detected in this PR.

Recommendation: Add tests to verify the fix using the write-tests-agent:

@copilot write tests for this PR

The agent will analyze the issue, determine the appropriate test type (UI test, device test, unit test, or XAML test), and create tests that verify the fix.



🚦 Gate Session4eb69f6 · fix-26103-Resaved the test image. · 2026-04-12 15:13 UTC

Gate Result: ⚠️ SKIPPED

No tests were detected in this PR.

Recommendation: Add tests to verify the fix using the write-tests-agent:

@copilot write tests for this PR

The agent will analyze the issue, determine the appropriate test type (UI test, device test, unit test, or XAML test), and create tests that verify the fix.


@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 12, 2026

🤖 AI Summary

👋 @BagavathiPerumal — new AI review results are available. Please review the latest session below.

📊 Review Session713f1e8 · fix-26108-Made code changes to bypass the extension method guard and Inline code comments have been removed to align with the repository style. · 2026-04-13 13:22 UTC
🔍 Pre-Flight — Context & Validation

Issue: #26103 - TabbedPage - ScrollView not allowing scrolling when it should
Related Issues: #11402 - TabbedPage resize hides bottom content (Windows); #20028 - Grid overflows child ContentPage of TabbedPage on resize (Windows)
PR: #26217 - [Windows] TabbedPage: Refresh layout when NavigationView size changes
Author: BagavathiPerumal (community/Syncfusion partner)
Platforms Affected: Windows (all issues are Windows-only)
Files Changed: 1 implementation, 6 snapshots (no new tests)

Key Findings

  • Root cause: OnNavigationViewSizeChanged called this.Arrange(_navigationView) which uses _navigationView.ActualWidth/Height with a "no-op if unchanged" guard. Layout was not being invalidated before arrange, causing stale measure values and incorrect layout after window resize.
  • Fix: Adds this.InvalidateMeasure() before Arrange(), and switches from this.Arrange(_navigationView) to this.Arrange(new Rect(0, 0, e.NewSize.Width, e.NewSize.Height)).
  • Unresolved PureWeen concern: "The call to arrange inside the SizeChanged method here doesn't seem correct. If you remove the call to this.Arrange and leave this.InvalidateMeasure does this all still work?"
  • Author says Arrange() is needed to prevent ChangingToNewMauiContextDoesntCrash device test from timing out.
  • 6 Windows snapshot files were regenerated because the fix corrects layout alignment in TabbedPage.
  • Gate: ⚠️ SKIPPED — no tests detected in this PR (no new test files; window resize can't be automated per author).
  • Prior agent review: ⚠️ REQUEST CHANGES (same key concerns remain)

Edge Cases

  • this.Arrange(new Rect(0,0, e.NewSize.Width, e.NewSize.Height)) arranges TabbedPage with the _navigationView's new size — not the window size. In the FlyoutPage-embedded scenario, NavigationView IS the TabbedPage's root, so sizes match.
  • Old code had a guard (if (!view.Frame.Equals(rect)) view.Arrange(rect)) preventing redundant arrange calls. New code always arranges unconditionally — potential performance/recursion risk.
  • SizeChanged is subscribed only in SetupNavigationView() when _navigationView.TopNavArea == null (template not yet applied). The handler is unsubscribed in OnHandlerDisconnected. So it fires only briefly during initial setup.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #26217 Add InvalidateMeasure() before Arrange(), use e.NewSize directly ⏳ PENDING TabbedPage.Windows.cs + 6 snapshots Original PR

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (opus-4.6) InvalidateMeasure() alone — no Arrange() ✅ PASS (59/59) TabbedPage.Windows.cs Simplest; directly addresses PureWeen's concern
2 try-fix (sonnet-4.6) Unsubscribe SizeChanged after OnApplyTemplateFinished; trust layout pipeline ✅ PASS (59/59) TabbedPage.Windows.cs Addresses lifecycle bug but may not fix actual window-resize scenario
3 try-fix (gpt-5.3-codex) Remove frame-equality guard in FrameworkElementExtensions.Arrange globally ❌ FAIL (53/59) FrameworkElementExtensions.cs Too broad — caused visual regressions in 6 snapshot tests
4 try-fix (gpt-5.4) ForceLayout() in OnNavigationViewSizeChanged ✅ PASS (59/59) TabbedPage.Windows.cs ForceLayout() is a legacy/deprecated API path
PR PR #26217 InvalidateMeasure() + Arrange(new Rect(0,0,w,h)) ⏳ Gate skipped TabbedPage.Windows.cs + 6 snapshots Original PR; has unresolved reviewer concern

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 NO NEW IDEAS Design space exhausted; noted possible combination of Attempt 1 + 2

Exhausted: Yes
Selected Fix: Attempt 1 — InvalidateMeasure() alone

Reason: Simplest passing alternative. Directly addresses PureWeen's concern. InvalidateMeasure() triggers the layout system to re-measure and re-arrange naturally — no questionable Arrange() in a SizeChanged handler. Passes all 59 TabbedPage tests. The PR should be updated to remove the Arrange() call.

Note on Attempt 2: While it passed tests, it unsubscribes the SizeChanged handler after initialization — which may mean actual runtime window-resize events are not handled. Snapshot tests only test static layout. Attempt 1 is safer as it keeps the handler for real resizes.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE 3 linked issues (#26103, #11402, #20028); Windows-only layout bug
Gate ⚠️ SKIPPED No tests detected in PR (window resize not automatable per author)
Try-Fix ✅ COMPLETE 4 attempts; 3 passing, 1 failing
Report ✅ COMPLETE

Summary

This PR fixes a long-standing Windows TabbedPage layout bug where resizing the window causes incorrect layout measurements (ScrollView can't scroll, content clipped, Grid overflows). The core fix is in OnNavigationViewSizeChanged in TabbedPage.Windows.cs. The fix is directionally correct, but an alternative approach found by exploration is simpler and directly addresses the main unresolved reviewer concern.

Root Cause

OnNavigationViewSizeChanged called this.Arrange(_navigationView) via FrameworkElementExtensions.Arrange(IView, FrameworkElement), which:

  1. Uses stale ActualWidth/Height values (not yet updated at the time of the event)
  2. Has a "no-op if unchanged" guard — silently skips the arrange if view.Frame already equals the stale rect

The result: layout measurements are never invalidated before the next arrange pass, so child views (ScrollView, Grid) render with the old dimensions.

Fix Quality

PR's approach: Adds this.InvalidateMeasure() before changing this.Arrange(_navigationView) to this.Arrange(new Rect(0, 0, e.NewSize.Width, e.NewSize.Height)).

Concerns with PR's fix:

  1. Unresolved reviewer concern (PureWeen): "The call to arrange inside the SizeChanged method here doesn't seem correct. If you remove the call to this.Arrange and leave this.InvalidateMeasure does this all still work?" — this thread is still open.
  2. Try-Fix confirms PureWeen is right: Attempt 1 (InvalidateMeasure() alone, no Arrange()) passes all 59 TabbedPage tests. The explicit Arrange(new Rect(...)) in a SizeChanged handler is unnecessary.
  3. Unconditional Arrange: The PR removes the "only if changed" guard, so Arrange is called on every resize even if size hasn't changed — minor performance concern.
  4. 6 snapshot files updated — expected, since the layout fix changes how the TabbedPage renders. The updates appear visually correct.
  5. No new tests — Gate was skipped. The author reasonably argues window resize can't be automated.

Recommended Change

The PR should be simplified to:

void OnNavigationViewSizeChanged(object sender, SizeChangedEventArgs e)
{
    if (_navigationView != null)
        this.InvalidateMeasure();
}

This is the minimal fix that:

  • Directly addresses PureWeen's concern
  • Removes the questionable Arrange() call from a SizeChanged handler
  • Passes all 59 TabbedPage UI tests (verified empirically)
  • Trusts WinUI's layout system to handle re-arrangement after measure invalidation

Try-Fix Comparison

See try-fix/content.md for full table. Summary: InvalidateMeasure() alone (Attempt 1) is the best alternative — simpler, equally effective, answers the open reviewer question.

Selected Fix: Attempt 1 (not PR's fix)



📊 Review Session4eb69f6 · fix-26103-Resaved the test image. · 2026-04-12 15:56 UTC
🔍 Pre-Flight — Context & Validation

Issue: #26103 - TabbedPage - ScrollView not allowing scrolling when it should
Related Issues: #11402 - TabbedPage app on resize hides page bottom content; #20028 - TabbedPage layout misalignment on window resize
PR: #26217 - [Windows] TabbedPage: Refresh layout when NavigationView size changes
Platforms Affected: Windows (fix), snapshot updates affect Windows UI tests
Files Changed: 1 implementation, 6 test snapshots (no new tests added)
Gate Result: ⚠️ SKIPPED — no tests detected

Key Findings

  • Root cause: OnNavigationViewSizeChanged only called this.Arrange(_navigationView) without first invalidating the measure cache, so the layout pass used stale measurements after a window resize, causing scroll content to be clipped.
  • Fix: The PR adds this.InvalidateMeasure() before the existing this.Arrange(_navigationView) call inside the null-guard block.
  • Concern from reviewer (PureWeen): Asked whether Arrange() is even needed and whether InvalidateMeasure() alone suffices. The PR author claims removing Arrange() causes the ChangingToNewMauiContextDoesntCrash test to time out. This claim has not been independently verified.
  • Comment from inline reviewer (jsuarezruiz): Suggested adding a test using Appium Window.Maximize()/Window.Minimize() to reproduce. Author attempted but could not successfully reproduce via Appium test.
  • Reporter confirmed fix works: Issue reporter (Mark-NC001) tested PR and confirmed ScrollView now scrolls to the bottom correctly.
  • No new tests added: PR author argues window resize is not automatable. Gate skipped since no tests present.
  • Snapshot changes: 6 TabbedPage Windows snapshot baselines were updated because the layout is now more correct (items better aligned). This is an expected side-effect of the fix.
  • Inline comments: One unresolved review thread — PureWeen's question about whether Arrange() is still needed.
  • Prior agent reviews: None (only an AI summary posted by the PR author, not an agent review).

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #26217 Add InvalidateMeasure() before existing Arrange(_navigationView) call in OnNavigationViewSizeChanged ⚠️ SKIPPED (no tests) TabbedPage.Windows.cs + 6 snapshots Original PR; Arrange() kept to prevent test timeout per author

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Replace this.Arrange(_navigationView) with _navigationView.InvalidateMeasure() to let WinUI's layout cycle handle re-arrangement ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly, unit tests pass; device tests unavailable
2 try-fix (claude-sonnet-4.6) Directly measure+arrange _displayedPage using _navigationFrame.ActualWidth/Height (content area dimensions) ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly; device tests unavailable
3 try-fix (gpt-5.3-codex) Dual invalidation (InvalidateMeasure + InvalidateArrange) on both this and _navigationView, no explicit Arrange() ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly; device tests unavailable
4 try-fix (gpt-5.4) _displayedPage.InvalidateMeasure() + platform view fallback invalidation ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly; device tests unavailable
PR PR #26217 Add this.InvalidateMeasure() before existing this.Arrange(_navigationView) call in OnNavigationViewSizeChanged ⚠️ SKIPPED (Gate) TabbedPage.Windows.cs + 6 snapshots Original PR

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes Key insight: this.Arrange(_navigationView) uses FrameworkElementExtensions.Arrange (line 184) which has a guard: if (!view.Frame.Equals(rect)). This means if MAUI Frame already matches the current NavigationView dimensions, the arrange is silently skipped. New ideas: (1) Bypass guard with e.NewSize directly calling VisualElement.Arrange(Rect) unconditionally; (2) Defer layout to next dispatcher tick via DispatcherQueue.TryEnqueue; (3) Subscribe to _navigationFrame.SizeChanged instead; (4) Use WinUI _navigationView.UpdateLayout()

Exhausted: Yes — all 4 models + cross-pollination completed

Key Infrastructure Finding: All Windows device tests (including ChangingToNewMauiContextDoesntCrash) blocked with WindowsAppSDKSelfContained requires a supported Windows architecture. Unit tests pass for all approaches.

Selected Fix: PR #26217 — No attempt could be validated over the PR's fix due to device test infrastructure unavailability. The PR's approach (InvalidateMeasure() + Arrange()) is the most tested fix (reporter confirmed working). However, PureWeen's concern about the Arrange() call inside SizeChanged remains unresolved. The cross-pollination insight about the FrameworkElementExtensions.Arrange guard (line 184) is relevant — if the guard is the real root cause, bypassing it with e.NewSize might be cleaner.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #26103 (Windows TabbedPage ScrollView clipping on resize) + related #11402, #20028
Gate ⚠️ SKIPPED No tests detected in PR
Try-Fix ⚠️ BLOCKED 4 attempts, 0 passing — Windows device test infrastructure unavailable (WindowsAppSDKSelfContained requires a supported Windows architecture)
Report ✅ COMPLETE

Selected Fix: PR (no alternative could be validated)


Summary

PR #26217 fixes a Windows-only layout bug where TabbedPage containing a ScrollView fails to correctly update its layout after window resize. The fix adds this.InvalidateMeasure() before the existing this.Arrange(_navigationView) call in OnNavigationViewSizeChanged. This is a 5-line change to TabbedPage.Windows.cs plus 6 updated snapshot baselines.

The fix is confirmed working by the issue reporter. However, there is an unresolved reviewer concern from PureWeen about whether the Arrange() call inside SizeChanged is necessary and correct, and no new automated tests were added.


Root Cause

When the window is resized, the WinUI NavigationView.SizeChanged event fires. The original handler called this.Arrange(_navigationView), which uses the FrameworkElementExtensions.Arrange(IView, FrameworkElement) extension method (defined in FrameworkElementExtensions.cs:184). This extension method:

  1. Builds a Rect(0, 0, frameworkElement.ActualWidth, frameworkElement.ActualHeight) from current NavigationView dimensions
  2. Only calls view.Arrange(rect) if !view.Frame.Equals(rect) (guard to prevent redundant arranges)

The bug: view.Arrange(rect) does NOT call InvalidateMeasure() first. So when the layout pass runs, the MAUI measure cache still contains stale sizes from before the resize. The ScrollView child's DesiredSize is never re-measured with the new window dimensions, causing clipping.

The PR's fix correctly adds this.InvalidateMeasure() before this.Arrange() so that stale measurements are discarded before the arrangement pass runs.


Fix Quality

Strengths:

  • ✅ Fix is minimal and targeted (5 lines changed in one method)
  • ✅ Addresses the correct root cause (stale measure cache)
  • ✅ Confirmed working by issue reporter (Mark-NC001)
  • ✅ Snapshot updates are expected and correct (layout is now more accurate)

Concerns requiring author response:

  1. ❓ Is this.Arrange(_navigationView) still needed? — PureWeen asked whether InvalidateMeasure() alone suffices. The PR author claims removing Arrange() causes ChangingToNewMauiContextDoesntCrash device test to time out. This claim should be independently verified. Calling Arrange() inside a SizeChanged handler of the same view is architecturally unusual and could cause layout re-entrancy issues.

  2. 🔍 Consider bypassing the extension method guard — The FrameworkElementExtensions.Arrange extension has a frame-equality guard. If the new size matches the MAUI Frame (e.g., when window is restored to its original size), the arrange is silently skipped. A more robust approach may be: this.InvalidateMeasure(); this.Arrange(new Graphics.Rect(0, 0, e.NewSize.Width, e.NewSize.Height)); using e.NewSize from the event args to bypass the guard unconditionally.

  3. ⚠️ No new tests — The Gate was skipped because no tests were added. The reviewer (jsuarezruiz) suggested using Appium's app.Driver.Manage().Window.Maximize() / Minimize() to test window resize behavior. This is technically feasible and should be explored.

  4. 🧹 Inline code comments — The two inline comments added (// Ensure TabbedPage layout responds to NavigationView size changes, // Complete layout to fix frame dimensions) do not add significant clarity over the code itself and should follow the repo convention of minimal comments.


Recommendation Details

The fix is functionally correct based on reporter confirmation and code analysis. However, the open reviewer question about Arrange() necessity plus the absence of tests leads to a REQUEST CHANGES recommendation. The author should:

  1. Address PureWeen's concern about Arrange() vs InvalidateMeasure() alone with a reproducible test or CI evidence
  2. Consider using e.NewSize to bypass the extension guard for robustness
  3. Explore adding a window-resize UI test as jsuarezruiz suggested
  4. Remove or simplify the inline code comments to match repo style

…Inline code comments have been removed to align with the repository style.
@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

BagavathiPerumal commented Apr 13, 2026

🤖 AI Summary

👋 @BagavathiPerumal — new AI review results are available. Please review the latest session below.

📊 Review Session4eb69f6 · fix-26103-Resaved the test image. · 2026-04-12 15:56 UTC
🔍 Pre-Flight — Context & Validation
Issue: #26103 - TabbedPage - ScrollView not allowing scrolling when it should Related Issues: #11402 - TabbedPage app on resize hides page bottom content; #20028 - TabbedPage layout misalignment on window resize PR: #26217 - [Windows] TabbedPage: Refresh layout when NavigationView size changes Platforms Affected: Windows (fix), snapshot updates affect Windows UI tests Files Changed: 1 implementation, 6 test snapshots (no new tests added) Gate Result: ⚠️ SKIPPED — no tests detected

Key Findings

  • Root cause: OnNavigationViewSizeChanged only called this.Arrange(_navigationView) without first invalidating the measure cache, so the layout pass used stale measurements after a window resize, causing scroll content to be clipped.
  • Fix: The PR adds this.InvalidateMeasure() before the existing this.Arrange(_navigationView) call inside the null-guard block.
  • Concern from reviewer (PureWeen): Asked whether Arrange() is even needed and whether InvalidateMeasure() alone suffices. The PR author claims removing Arrange() causes the ChangingToNewMauiContextDoesntCrash test to time out. This claim has not been independently verified.
  • Comment from inline reviewer (jsuarezruiz): Suggested adding a test using Appium Window.Maximize()/Window.Minimize() to reproduce. Author attempted but could not successfully reproduce via Appium test.
  • Reporter confirmed fix works: Issue reporter (Mark-NC001) tested PR and confirmed ScrollView now scrolls to the bottom correctly.
  • No new tests added: PR author argues window resize is not automatable. Gate skipped since no tests present.
  • Snapshot changes: 6 TabbedPage Windows snapshot baselines were updated because the layout is now more correct (items better aligned). This is an expected side-effect of the fix.
  • Inline comments: One unresolved review thread — PureWeen's question about whether Arrange() is still needed.
  • Prior agent reviews: None (only an AI summary posted by the PR author, not an agent review).

Fix Candidates

Source Approach Test Result Files Changed Notes

PR PR #26217 Add InvalidateMeasure() before existing Arrange(_navigationView) call in OnNavigationViewSizeChanged ⚠️ SKIPPED (no tests) TabbedPage.Windows.cs + 6 snapshots Original PR; Arrange() kept to prevent test timeout per author
🔧 Fix — Analysis & Comparison

Fix Candidates

Source Approach Test Result Files Changed Notes

1 try-fix (claude-opus-4.6) Replace this.Arrange(_navigationView) with _navigationView.InvalidateMeasure() to let WinUI's layout cycle handle re-arrangement ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly, unit tests pass; device tests unavailable
2 try-fix (claude-sonnet-4.6) Directly measure+arrange _displayedPage using _navigationFrame.ActualWidth/Height (content area dimensions) ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly; device tests unavailable
3 try-fix (gpt-5.3-codex) Dual invalidation (InvalidateMeasure + InvalidateArrange) on both this and _navigationView, no explicit Arrange() ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly; device tests unavailable
4 try-fix (gpt-5.4) _displayedPage.InvalidateMeasure() + platform view fallback invalidation ⚠️ BLOCKED TabbedPage.Windows.cs Compiles cleanly; device tests unavailable
PR PR #26217 Add this.InvalidateMeasure() before existing this.Arrange(_navigationView) call in OnNavigationViewSizeChanged ⚠️ SKIPPED (Gate) TabbedPage.Windows.cs + 6 snapshots Original PR

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes Key insight: this.Arrange(_navigationView) uses FrameworkElementExtensions.Arrange (line 184) which has a guard: if (!view.Frame.Equals(rect)). This means if MAUI Frame already matches the current NavigationView dimensions, the arrange is silently skipped. New ideas: (1) Bypass guard with e.NewSize directly calling VisualElement.Arrange(Rect) unconditionally; (2) Defer layout to next dispatcher tick via DispatcherQueue.TryEnqueue; (3) Subscribe to _navigationFrame.SizeChanged instead; (4) Use WinUI _navigationView.UpdateLayout()
Exhausted: Yes — all 4 models + cross-pollination completed

Key Infrastructure Finding: All Windows device tests (including ChangingToNewMauiContextDoesntCrash) blocked with WindowsAppSDKSelfContained requires a supported Windows architecture. Unit tests pass for all approaches.

Selected Fix: PR #26217 — No attempt could be validated over the PR's fix due to device test infrastructure unavailability. The PR's approach (InvalidateMeasure() + Arrange()) is the most tested fix (reporter confirmed working). However, PureWeen's concern about the Arrange() call inside SizeChanged remains unresolved. The cross-pollination insight about the FrameworkElementExtensions.Arrange guard (line 184) is relevant — if the guard is the real root cause, bypassing it with e.NewSize might be cleaner.

📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #26103 (Windows TabbedPage ScrollView clipping on resize) + related #11402, #20028
Gate ⚠️ SKIPPED No tests detected in PR
Try-Fix ⚠️ BLOCKED 4 attempts, 0 passing — Windows device test infrastructure unavailable (WindowsAppSDKSelfContained requires a supported Windows architecture)
Report ✅ COMPLETE
Selected Fix: PR (no alternative could be validated)

Summary

PR #26217 fixes a Windows-only layout bug where TabbedPage containing a ScrollView fails to correctly update its layout after window resize. The fix adds this.InvalidateMeasure() before the existing this.Arrange(_navigationView) call in OnNavigationViewSizeChanged. This is a 5-line change to TabbedPage.Windows.cs plus 6 updated snapshot baselines.

The fix is confirmed working by the issue reporter. However, there is an unresolved reviewer concern from PureWeen about whether the Arrange() call inside SizeChanged is necessary and correct, and no new automated tests were added.

Root Cause

When the window is resized, the WinUI NavigationView.SizeChanged event fires. The original handler called this.Arrange(_navigationView), which uses the FrameworkElementExtensions.Arrange(IView, FrameworkElement) extension method (defined in FrameworkElementExtensions.cs:184). This extension method:

  1. Builds a Rect(0, 0, frameworkElement.ActualWidth, frameworkElement.ActualHeight) from current NavigationView dimensions
  2. Only calls view.Arrange(rect) if !view.Frame.Equals(rect) (guard to prevent redundant arranges)

The bug: view.Arrange(rect) does NOT call InvalidateMeasure() first. So when the layout pass runs, the MAUI measure cache still contains stale sizes from before the resize. The ScrollView child's DesiredSize is never re-measured with the new window dimensions, causing clipping.

The PR's fix correctly adds this.InvalidateMeasure() before this.Arrange() so that stale measurements are discarded before the arrangement pass runs.

Fix Quality

Strengths:

  • ✅ Fix is minimal and targeted (5 lines changed in one method)
  • ✅ Addresses the correct root cause (stale measure cache)
  • ✅ Confirmed working by issue reporter (Mark-NC001)
  • ✅ Snapshot updates are expected and correct (layout is now more accurate)

Concerns requiring author response:

  1. ❓ Is this.Arrange(_navigationView) still needed? — PureWeen asked whether InvalidateMeasure() alone suffices. The PR author claims removing Arrange() causes ChangingToNewMauiContextDoesntCrash device test to time out. This claim should be independently verified. Calling Arrange() inside a SizeChanged handler of the same view is architecturally unusual and could cause layout re-entrancy issues.
  2. 🔍 Consider bypassing the extension method guard — The FrameworkElementExtensions.Arrange extension has a frame-equality guard. If the new size matches the MAUI Frame (e.g., when window is restored to its original size), the arrange is silently skipped. A more robust approach may be: this.InvalidateMeasure(); this.Arrange(new Graphics.Rect(0, 0, e.NewSize.Width, e.NewSize.Height)); using e.NewSize from the event args to bypass the guard unconditionally.
  3. ⚠️ No new tests — The Gate was skipped because no tests were added. The reviewer (jsuarezruiz) suggested using Appium's app.Driver.Manage().Window.Maximize() / Minimize() to test window resize behavior. This is technically feasible and should be explored.
  4. 🧹 Inline code comments — The two inline comments added (// Ensure TabbedPage layout responds to NavigationView size changes, // Complete layout to fix frame dimensions) do not add significant clarity over the code itself and should follow the repo convention of minimal comments.

Recommendation Details

The fix is functionally correct based on reporter confirmation and code analysis. However, the open reviewer question about Arrange() necessity plus the absence of tests leads to a REQUEST CHANGES recommendation. The author should:

  1. Address PureWeen's concern about Arrange() vs InvalidateMeasure() alone with a reproducible test or CI evidence
  2. Consider using e.NewSize to bypass the extension guard for robustness
  3. Explore adding a window-resize UI test as jsuarezruiz suggested
  4. Remove or simplify the inline code comments to match repo style

I’ve reviewed and updated the changes based on the latest findings. Specifically:

  • The reason for using Arrange(_navigationView) has already been updated in the comment. It is currently required to prevent failures in ChangingToNewMauiContextDoesntCrash.
  • The code has been updated based on the suggestion to bypass the extension method guard.
  • The reason for not adding a test case for this fix has already been mentioned in the PR template and also clarified in the comments.
  • Inline code comments have been removed to align with the repository style.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-tabbedpage TabbedPage community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates 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