Skip to content

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

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

[Windows] TabbedPage: Refresh layout when NavigationView size changes#26217
BagavathiPerumal wants to merge 5 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

@BagavathiPerumal BagavathiPerumal changed the title Fix for TabbedPage - ScrollView not allowing scrolling when it should [Windows]Fix for TabbedPage - ScrollView not allowing scrolling when it should Dec 8, 2025
@kubaflo kubaflo added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Mar 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

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

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 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).

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 20, 2026

🤖 AI Summary

📊 Expand Full Reviewe4a80c4 · fix-26103-Resaved the test image.
🔍 Pre-Flight — Context & Validation

Issue: #26103 - TabbedPage - ScrollView not allowing scrolling when it should (also #11402 - TabbedPage App on resize hides page bottom content; #20028)
PR: #26217 - [Windows] TabbedPage: Refresh layout when NavigationView size changes
Platforms Affected: Windows (primary); snapshot updates affect Windows UI tests
Files Changed: 1 implementation (TabbedPage.Windows.cs), 6 snapshot PNG files

Key Findings

  • Root issue: when the WinUI NavigationView hosting TabbedPage tabs changes size (e.g., window resize), MAUI's virtual view layout is not recalculated, causing child ScrollView to use stale dimensions and clip/hide content.
  • Original code in OnNavigationViewSizeChanged had only this.Arrange(_navigationView), which calls a Windows extension method that directly sets the virtual view's Frame to the NavigationView's ActualWidth × ActualHeight.
  • The PR adds this.InvalidateMeasure() before the existing Arrange() call to trigger MAUI's normal measure/arrange cycle.
  • Key reviewer concern (PureWeen): "If you remove this.Arrange and leave InvalidateMeasure does this all still work? The call to Arrange inside SizeChanged doesn't seem correct."
  • Author says: Removing Arrange() (keeping only InvalidateMeasure()) causes ChangingToNewMauiContextDoesntCrash device test to consistently fail with TimeoutException.
  • SizeChanged is subscribed at setup time when TopNavArea == null but is NOT unsubscribed when OnApplyTemplateFinished fires — remains active for the full TabbedPage lifetime, firing on every window resize.
  • this.Arrange(_navigationView) (extension in FrameworkElementExtensions.cs) creates Rect(0,0,ActualWidth,ActualHeight) and calls IView.Arrange(rect) only if current Frame differs — bypasses MAUI's measure phase.
  • 6 snapshot tests needed baseline updates because the fix improves alignment of TabbedPage items.
  • Issue reporter confirmed (TabbedPage - ScrollView not allowing scrolling when it should #26103) the PR fixes their problem.
  • No new automated test added; author argues window-resize scenario requires manual testing.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #26217 Add InvalidateMeasure() before existing Arrange(_navigationView) in OnNavigationViewSizeChanged ⏳ PENDING TabbedPage.Windows.cs + 6 snapshot PNGs Original PR

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Remove Arrange(), keep only InvalidateMeasure() ⚠️ BLOCKED (no Windows device test runner on agent) TabbedPage.Windows.cs Reviewer's preferred approach; code change is clean
2 try-fix (claude-sonnet-4.6) Split SizeChanged: keep Arrange() for init path, use InvalidateMeasure() for resize-only handler (unsubscribe in OnApplyTemplateFinished) ⚠️ BLOCKED (same Windows device test infra issue) TabbedPage.Windows.cs Architecturally sound; cleanly addresses reviewer concern
3 try-fix (gpt-5.3-codex) Replace Arrange() with InvalidateMeasure() + native platformView.InvalidateMeasure()/InvalidateArrange()/UpdateLayout() ❌ FAIL (infra blocker: WindowsAppSDK arch issue) TabbedPage.Windows.cs Interesting native approach; blocked by build infra
4 try-fix (gpt-4.1) _templateApplied flag: Arrange if not yet applied, InvalidateMeasure after ❌ FAIL (same infra build blocker; code compiled) TabbedPage.Windows.cs Functionally same as attempt 2 but with flag
5 try-fix (claude-opus-4.6, cross-poll) Deferred Arrange via Dispatcher.Dispatch() — synchronous InvalidateMeasure + async Arrange ⚠️ BLOCKED (same infra; code compiled) TabbedPage.Windows.cs Addresses PureWeen's re-entrancy concern
PR PR #26217 Add InvalidateMeasure() before existing Arrange(_navigationView) ⏳ PENDING (Gate skipped) TabbedPage.Windows.cs + 6 PNGs Original PR

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes Deferred Arrange via Dispatcher.Dispatch() → became attempt 5
claude-sonnet-4.6 2 Yes Subscribe _navigationFrame.SizeChanged (content area) instead of nav view; cascade InvalidateMeasure to CurrentPage
gpt-5.3-codex 2 Yes One-shot LayoutUpdated/EffectiveViewportChanged hook against _navigationFrame bounds
gpt-4.1 2 Yes Loaded event or CompositionTarget.Rendering one-shot hook for post-render layout pass
All models 3 No All ideas exhausted given infra constraints

Exhausted: Yes — 5 attempts, 2 cross-poll rounds. All blocked by WindowsAppSDKSelfContained build infra on this agent. No approach can be empirically validated here.

Selected Fix: PR #26217's fix — it is the only fix validated to work (by the PR author and issue reporter), and all alternatives have stronger architectural merit but are unverifiable on this agent. However, see report for recommended changes.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Windows-only TabbedPage layout bug; #26103, #11402, #20028
Gate ⚠️ SKIPPED No tests detected in this PR
Try-Fix ✅ COMPLETE 5 attempts, 0 passing (all BLOCKED by Windows AppSDK build infra on agent)
Report ✅ COMPLETE

Summary

PR #26217 fixes a real, verified Windows bug: when the WinUI NavigationView hosting TabbedPage tabs resizes (window resize), MAUI's virtual view layout was not recalculated, causing ScrollView and other content to clip or hide the bottom of the page. The issue reporter confirmed the fix works. The code change is small — 4 lines added to OnNavigationViewSizeChanged. Six snapshot baselines were updated to reflect the improved TabbedPage alignment.

The PR has open reviewer concern from PureWeen that needs resolution before merge.

Root Cause

OnNavigationViewSizeChanged previously only called this.Arrange(_navigationView), which sets the MAUI virtual view's Frame directly from the NavigationView's ActualWidth × ActualHeight. This bypasses MAUI's measure phase. When the window is resized, the virtual view's dimensions were updated via Arrange, but child views (e.g., ScrollView) did not trigger their own re-measure cycle, causing stale layout dimensions to persist.

Fix Quality

What the PR does: Adds this.InvalidateMeasure() before the existing this.Arrange(_navigationView) call. This is a minimal, targeted change that triggers MAUI's re-measure cycle and then forces a synchronous arrangement.

Concerns:

  1. Arrange() inside SizeChanged is architecturally questionable — Reviewer PureWeen explicitly asked: "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?" The author responded that removing Arrange() breaks the ChangingToNewMauiContextDoesntCrash device test (TimeoutException). However, this claim was not verified with a test run in this review session (Windows device test infra unavailable on this agent). The PR should demonstrate — via a CI run or test output — that Arrange() is genuinely required and not just coincidentally masking a timing issue.

  2. SizeChanged handler has dual purpose, neither documented — The handler was originally added as an initialization fallback (fires when TopNavArea == null at setup). It is subscribed in SetupNavigationView() but never unsubscribed in OnApplyTemplateFinished, so it continues firing on every window resize for the TabbedPage's full lifetime. This implicit dual responsibility (initialization + resize) is undocumented and fragile. A cleaner approach (explored in Try-Fix Attempt 2) would: unsubscribe the handler in OnApplyTemplateFinished after initialization completes, and add a new dedicated resize handler that calls only InvalidateMeasure() — addressing the reviewer's concern while preserving the Arrange() call for the initialization path where ChangingToNewMauiContextDoesntCrash depends on it.

  3. No new test added — The PR author argues window-resize behavior cannot be automated. Reviewer jsuarezruiz suggested using Appium's Driver.Manage().Window.Maximize() / Minimize() APIs to reproduce the resize scenario. This approach is feasible and should be attempted. The Gate was skipped due to no tests being detected.

  4. Snapshot updates look correct — Six Windows UI test snapshots were updated; the diffs show improved alignment of TabbedPage elements, consistent with the layout fix.

  5. Comments are low quality"// Ensure TabbedPage layout responds to NavigationView size changes" and "// Complete layout to fix frame dimensions" don't explain why both calls are needed together, or why Arrange() is needed at all given InvalidateMeasure() was also added. The comments should explain the dual-purpose design.

Recommended Changes

  1. Verify Arrange() is necessary: Either (a) demonstrate via a CI run that removing Arrange() (keeping only InvalidateMeasure()) breaks ChangingToNewMauiContextDoesntCrash, or (b) implement the split-handler approach (unsubscribe SizeChanged in OnApplyTemplateFinished, add resize-only handler with InvalidateMeasure()).

  2. Separate the initialization and resize paths (preferred by reviewer):

    void OnApplyTemplateFinished(object? sender, EventArgs e)
    {
        UpdateValuesWaitingForNavigationView();
        if (sender is MauiNavigationView mnv)
        {
            mnv.OnApplyTemplateFinished -= OnApplyTemplateFinished;
            mnv.SizeChanged -= OnNavigationViewSizeChanged;
            mnv.SizeChanged += OnNavigationViewResized;
        }
    }
    
    void OnNavigationViewSizeChanged(object sender, SizeChangedEventArgs e)
    {
        // Initialization path only — fired before template is applied.
        if (_navigationView != null)
            this.Arrange(_navigationView);
    }
    
    void OnNavigationViewResized(object sender, SizeChangedEventArgs e)
    {
        // Post-initialization resize — let MAUI layout system handle it.
        if (_navigationView != null)
            this.InvalidateMeasure();
    }
    // Also unsubscribe OnNavigationViewResized in OnHandlerDisconnected.
  3. Add a window-resize test using Appium Driver.Manage().Window.Size = new Size(...) to resize the window and verify ScrollView can scroll to the bottom.

  4. Improve or remove the inline comments — they don't add clarity in their current form.


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
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 29, 2026

🚦 Gate - Test Before and After Fix

📊 Expand Full Gatee4a80c4 · fix-26103-Resaved the test image.

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

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