Skip to content

[Windows] Fixed the ContentView clip is not updated when wrapping inside the Border#30408

Merged
kubaflo merged 2 commits into
dotnet:inflight/currentfrom
Ahamed-Ali:fix-30404
May 4, 2026
Merged

[Windows] Fixed the ContentView clip is not updated when wrapping inside the Border#30408
kubaflo merged 2 commits into
dotnet:inflight/currentfrom
Ahamed-Ali:fix-30404

Conversation

@Ahamed-Ali
Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause of the issue

  • On Windows, when a ContentView with a clip applied during the SizeChanged event is placed inside a Border, both the ContentView (through WrapperView) and the Border's content (via ContentPanel) attempt to apply their own clips to the same visual element.

  • The clip set in the ContentPanel.UpdateClip method of the Border is applied after the WrapperView has already set its clip, resulting in the Border overwriting the clip geometry originally applied.

Description of Change

  • The fix introduces a condition in ContentPanel.UpdateClip that checks:
    • Whether the content's visual already has a clip (visual.Clip is not null)
    • Whether the content is wrapped by a WrapperView (Content.Parent is WrapperView)
  • If both conditions are met, the ContentPanel skips applying its own clip to the content, allowing the ContentView’s clip (set by the WrapperView) to be preserved.

Issues Fixed

Fixes #30404

Tested the behaviour in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Screenshot

Before Issue Fix After Issue Fix

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Jul 3, 2025
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Hey there @@Ahamed-Ali! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jul 3, 2025
@Ahamed-Ali Ahamed-Ali marked this pull request as ready for review July 3, 2025 10:48
Copilot AI review requested due to automatic review settings July 3, 2025 10:48
@Ahamed-Ali Ahamed-Ali requested a review from a team as a code owner July 3, 2025 10:48
@Ahamed-Ali Ahamed-Ali requested review from jfversluis and rmarinho July 3, 2025 10:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes an issue on Windows where a ContentView clip applied during its SizeChanged event was being overwritten by its parent Border when wrapped inside a WrapperView.

  • Adds a guard in ContentPanel.UpdateClip to skip the border’s clip if the content already has one from WrapperView.
  • Introduces a new UI test in both the HostApp and Shared.Tests projects to verify the clip behavior for Issue #30404.

Reviewed Changes

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

File Description
src/Core/src/Platform/Windows/ContentPanel.cs Added a check to skip applying the Border’s clip when the content’s visual already has a clip.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30404.cs Added a UI test for Issue #30404, but it’s currently gated to iOS/Catalyst only.
src/Controls/tests/TestCases.HostApp/Issues/Issue30404.cs Created a sample page and ContentView that reproduces the clipping scenario for Windows.

Comment thread src/Core/src/Platform/Windows/ContentPanel.cs Outdated
@jsuarezruiz
Copy link
Copy Markdown
Contributor

/azp run MAUI-UITests-public

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@jsuarezruiz
Copy link
Copy Markdown
Contributor

/rebase

@MauiBot MauiBot 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 19, 2026
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.

Could you please check the AI's suggestion?

@MauiBot MauiBot added the s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) label Mar 20, 2026
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.

Could you please review the AI's summary?

@dotnet dotnet deleted a comment from MauiBot May 3, 2026
@dotnet dotnet deleted a comment from MauiBot May 3, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented May 3, 2026

🤖 AI Summary

👋 @Ahamed-Ali — new AI review results are available. Please review the latest session below.

📊 Review Session611443e · Updated the snap · 2026-05-03 22:50 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: WINDOWS · Base: main · Merge base: 1463c4c5

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue30404 Issue30404 ✅ FAIL — 576s ✅ PASS — 468s
🔴 Without fix — 🖥️ Issue30404: FAIL ✅ · 576s
  Determining projects to restore...
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 41.43 sec).
  Restored D:\a\1\s\src\Controls\Foldable\src\Controls.Foldable.csproj (in 462 ms).
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 41.78 sec).
  Restored D:\a\1\s\src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 3.77 sec).
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 31 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 25 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 101 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 24 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 4.05 sec).
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 24 ms).
  Restored D:\a\1\s\src\Controls\tests\TestCases.HostApp\Controls.TestCases.HostApp.csproj (in 722 ms).
  3 of 14 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:06:07.15
  Determining projects to restore...
  Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils\VisualTestUtils.csproj (in 826 ms).
  Restored D:\a\1\s\src\TestUtils\src\UITest.NUnit\UITest.NUnit.csproj (in 1.54 sec).
  Restored D:\a\1\s\src\TestUtils\src\UITest.Core\UITest.Core.csproj (in 3 ms).
  Restored D:\a\1\s\src\TestUtils\src\UITest.Appium\UITest.Appium.csproj (in 1.59 sec).
  Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils.MagickNet\VisualTestUtils.MagickNet.csproj (in 5.35 sec).
  Restored D:\a\1\s\src\Controls\tests\TestCases.WinUI.Tests\Controls.TestCases.WinUI.Tests.csproj (in 7.75 sec).
  Restored D:\a\1\s\src\Controls\tests\CustomAttributes\Controls.CustomAttributes.csproj (in 5 ms).
  Restored D:\a\1\s\src\TestUtils\src\UITest.Analyzers\UITest.Analyzers.csproj (in 9.3 sec).
  7 of 15 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
  Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
  VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
  VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
  UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
  UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
  UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
  UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
  Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/3/2026 7:02:09 PM FixtureSetup for Issue30404(Windows)
>>>>> 5/3/2026 7:02:19 PM ClipShouldApplyProperlyToTheContentOfBorder Start
>>>>> 5/3/2026 7:02:21 PM ClipShouldApplyProperlyToTheContentOfBorder Stop
>>>>> 5/3/2026 7:02:21 PM Log types: 
  Failed ClipShouldApplyProperlyToTheContentOfBorder [2 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: ClipShouldApplyProperlyToTheContentOfBorder.png (1.55% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

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

  Stack Trace:
     at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
   at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
   at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
   at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance, Boolean includeTitleBar) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
   at Microsoft.Maui.TestCases.Tests.Issues.Issue30404.ClipShouldApplyProperlyToTheContentOfBorder() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30404.cs:line 19
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

NUnit Adapter 4.5.0.0: Test execution complete
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.12]   Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.35]   Discovered:  Controls.TestCases.WinUI.Tests

Total tests: 1
     Failed: 1
Test Run Failed.
 Total time: 32.7990 Seconds

🟢 With fix — 🖥️ Issue30404: PASS ✅ · 468s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
  Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:05:50.25
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
  Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
  Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14003493
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
  UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
  UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
  UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
  VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
  VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
  Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/3/2026 7:09:59 PM FixtureSetup for Issue30404(Windows)
>>>>> 5/3/2026 7:10:08 PM ClipShouldApplyProperlyToTheContentOfBorder Start
>>>>> 5/3/2026 7:10:10 PM ClipShouldApplyProperlyToTheContentOfBorder Stop
  Passed ClipShouldApplyProperlyToTheContentOfBorder [1 s]
NUnit Adapter 4.5.0.0: Test execution complete
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.13]   Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.34]   Discovered:  Controls.TestCases.WinUI.Tests

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 26.6713 Seconds

📁 Fix files reverted (2 files)
  • eng/pipelines/ci-copilot.yml
  • src/Core/src/Platform/Windows/ContentPanel.cs

🧪 UI Tests — Category Detection

Detected UI test categories: Border,Shape,ViewBaseTests


🔍 Regression Cross-Reference

🔍 Regression Cross-Reference

🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.


🔍 Pre-Flight — Context & Validation

Issue: #30404 - [Windows] ContentView clip is not updated when wrapping inside the Border
PR: #30408 - [Windows] Fixed the ContentView clip is not updated when wrapping inside the Border
Platforms Affected: Windows (fix in ContentPanel.cs); test runs on Windows and Android (iOS/Catalyst excluded via #if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST due to #14164)
Files Changed: 1 implementation, 2 test (+ 2 binary snapshot files)

Key Findings

  • Root cause: ContentPanel.UpdateClip() (Border's native WinUI panel) fires after WrapperView.UpdateClip() and overwrites the Composition clip that WrapperView set on the ContentView's visual. The collision occurs because ContentPanel._content is a stale reference pointing to ContentView's inner ContentPanel after SetupContainer() moves it into a WrapperView.
  • Fix: A guard condition in ContentPanel.UpdateClip() checks visual.Clip != null && Content.Parent is WrapperView; if true, the method returns early and lets WrapperView's clip stand.
  • The fix is a correct workaround but is implicitly coupled to the side-effect that ContentPanel._content is never updated after SetupContainer() moves the child.
  • Test uses screenshot verification (VerifyScreenshot()) with category UITestCategories.Border.
  • Test is gated by #if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST — runs on Windows and Android.
  • Inline review comments from jsuarezruiz (MAUI maintainer) have all been resolved and addressed by the PR author.

Code Review Summary

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

Key code review findings:

  • ⚠️ ContentPanel.cs:188 — Guard relies on SetupContainer() side-effect (stale _content reference). If SetupContainer() is later fixed to update _content, the guard silently stops firing and the bug re-appears.
  • ⚠️ TestCases.Shared.Tests/Tests/Issues/Issue30404.cs:17 — Missing adjacent regression test: Border{StrokeShape=RoundRectangle} + ContentView{Clip=...}. The fix entirely skips Border's clip in this case without a snapshot to validate correctness.
  • 💡 TestCases.HostApp/Issues/Issue30404.cs:16VerifyScreenshot() without retryTimeout may capture before the Composition clip from SizeChanged has settled; consider retryTimeout: TimeSpan.FromSeconds(3).

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #30408 Skip ContentPanel.UpdateClip when visual.Clip != null && Content.Parent is WrapperView ✅ PASSED (Gate) ContentPanel.cs Original PR — workaround via early return

🔬 Code Review — Deep Analysis

Code Review — PR #30408

Independent Assessment

What this changes: On Windows, when a ContentView with a dynamically-set Clip (via SizeChanged) is nested inside a Border, ContentPanel.UpdateClip() (Border's native panel) overwrites the composition clip that WrapperView.UpdateClip() had applied to the ContentView's nested ContentPanel. The fix adds a short-circuit guard: if the content's composition visual already has a clip AND the content's WinUI parent is a WrapperView, skip applying the Border's clip.

Inferred motivation: A user's ContentView applies a RoundRectangleGeometry clip in SizeChanged. After layout, ContentPanel.UpdateClip() fires and overwrites that clip with the Border's own shape clip, making the ContentView's clip invisible.

How the guard actually works (key insight): When ContentView.Clip is set after initial layout, SetupContainer() is called, which moves ContentView's ContentPanel (the _content field of the Border's ContentPanel) from ContentPanel's direct children into a newly-created WrapperView. This makes ContentView's ContentPanel.Parent resolve to WrapperView. Meanwhile, ContentPanel._content is never updated — it still holds the stale reference to ContentView's ContentPanel (now nested inside WrapperView). The guard detects this stale state and bails out.


Reconciliation with PR Narrative

Author claims: "Both ContentView (through WrapperView) and the Border's ContentPanel attempt to apply their own clips to the same visual element." This is accurate. The PR description correctly identifies the collision. The fix correctly detects the post-SetupContainer() state.

Agreement/disagreement: The description is accurate but doesn't mention the root cause: ContentPanel._content is stale after SetupContainer(). The fix is a correct workaround but not a root-cause fix.


Findings

⚠️ Warning — Guard is coupled to SetupContainer() side-effect, not the intent

ContentPanel.cs:188: The condition Content.Parent is WrapperView works because SetupContainer() moves ContentView's ContentPanel out of the Border's ContentPanel.CachedChildren and into a WrapperView, but never updates ContentPanel._content to point to the WrapperView. If _content is ever corrected to point to WrapperView (which would be the real fix), Content.Parent would become ContentPanel again, the guard silently stops firing, and the original bug re-appears. The actual root fix is to update ContentPanel.Content to WrapperView once SetupContainer() wraps it.

⚠️ Warning — Adjacent scenario not tested: Border with StrokeShape + ContentView with Clip

Shared Tests Issue30404.cs:17: The test only covers a Border with the default Rectangle shape. The scenario of Border { StrokeShape = new RoundRectangle { CornerRadius = 20 } } wrapping a ContentView that sets Clip via SizeChanged is the regression-risk case. With this fix, ContentPanel.UpdateClip() is entirely skipped when the guard fires — including when Border has meaningful rounded-corner clipping. Since _content was already stale (pointing to the nested ContentPanel rather than WrapperView), the Border's clip was being applied to the wrong visual level anyway, but this should be explicitly validated with a snapshot.

💡 Suggestion — VerifyScreenshot() without retryTimeout for a clip set via SizeChanged

HostApp Issue30404.cs:16: The clip is applied in SizeChanged via WinUI Composition, which is asynchronous with respect to the rendering pipeline. WaitForElement("BorderContentLabel") confirms the label exists but not that the composition clip has settled. Consider VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(3)) to absorb any rendering lag.


CI Status

iOS DeviceTests (BlazorWebView, Essentials, Graphics) and some UI tests are failing. These appear pre-existing: the PR only modifies Windows platform code and test files, so iOS/Android failures are unrelated infrastructure issues. Windows device tests all pass ✅.


Devil's Advocate

  • Is the guard condition actually correct? Yes — after SetupContainer() runs, ContentView's ContentPanel is nested under WrapperView, so Content.Parent is WrapperView correctly identifies the post-wrap state. The check is functional.
  • Does skipping Border's clip break visual output? Because _content was already stale (pointing to the inner ContentPanel, not the WrapperView that is actually in the Border's visual tree), Border's clip was being applied at the wrong level anyway. Skipping it doesn't make the visual output worse in the common case.
  • Could the test pass trivially (the fix was unnecessary)? No — the label would be visible with or without the clip, but the shape of the clip would be wrong without the fix. The screenshot baseline captures that the rounded corners are correct.

Verdict: NEEDS_CHANGES

Confidence: medium
Summary: The fix correctly resolves the reported issue and the guard logic is sound given the current implementation. However, the guard is silently coupled to the SetupContainer() side-effect that leaves ContentPanel._content as a stale reference — if that root cause is addressed later, this guard breaks silently. A test covering the Border{StrokeShape=RoundRectangle} + ContentView{Clip=...} combination is missing, which is the highest-risk adjacent scenario for regression. Addressing both points would make this merge-ready.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Redirect border clip to WrapperView visual (GetBorderClipTarget()) apply ContentPanel clip to WrapperView's composition visual, two clips on two separate visuals PASS ContentPanel.cs Preserves border clipping for StrokeShape scenarios; better than PR
2 try-fix Deferred WrapperView re-apply via DispatcherQueue.TryEnqueue in WrapperView.OnChildSizeChanged PASS WrapperView.cs Fixes ordering without touching ContentPanel; single-frame flicker risk
3 try-fix Root-cause fix: update ContentPanel.Content to WrapperView in SetupContainer()/RemoveContainer() in ViewHandlerOfT.Windows.cs PASS ViewHandlerOfT.Windows.cs True root-cause fix; architecturally correct; most invasive
4 try-fix Explicit WrapperViewContentPanel clip-ownership contract flag PASS ContentPanel.cs, WrapperView.cs Cooperative contract; adds minor API surface
5 try-fix Composed clip intersection merge via Win2D CanvasGeometry.CombineWith(Intersect) PASS ContentPanel.cs Both clips preserved; correct for StrokeShape+ContentView.Clip combos
6 try-fix Clip ContentPanel's own visual instead of Content's visual FAIL ContentPanel.cs Rendering artifacts; IRoundRectangle interface limitation
PR PR #30408 Guard if (visual.Clip != null && Content.Parent is WrapperView) return; PASSED (Gate) ContentPanel.cs Original PR fragile coupling to SetupContainer() side-effect

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes Intermediate ContainerVisual for visual tree layering
claude-sonnet-4.6 2 Yes Composite clip merging (implemented as Attempt 5)
gpt-5.3-codex 2 Yes Intersection approach (implemented as Attempt 5)
gpt-5.4 2 Yes Intersection merge (same as Attempt 5)
claude-opus-4.6 3 Yes Intermediate clip-host element (complex beyond scope)
gpt-5.3-codex 3 Yes ClipCoordinator system (over-engineered)

Exhausted: Yes (max 3 rounds reached; remaining ideas are architectural changes beyond bug scope)
Selected Fix: try-fix-1 Redirect border clip to WrapperView visual
Reason: Simpler than try-fix-3 (root-cause), more correct than PR (preserves border StrokeShape clipping), no timing risk (vs try-fix-2), no API surface expansion (vs try-fix-4). Single file, minimal change.


📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE (with suggested alternative)

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #30404 confirmed Windows-only clip collision
Code Review NEEDS_CHANGES (medium) 2 warnings, 1 suggestion; no ❌ errors
Gate ✅ PASSED windows — tests fail without fix, pass with fix
Try-Fix ✅ COMPLETE 6 attempts (5 passing, 1 failing); 3 cross-pollination rounds
Report ✅ COMPLETE

Code Review Impact on Try-Fix

The code review's ⚠️ warning about the guard being coupled to SetupContainer()'s stale _content reference directly guided try-fix-3 (root-cause fix in ViewHandlerOfT.Windows.cs). The warning about the missing Border{StrokeShape=RoundRectangle} test case motivated try-fix-1's redirect approach (preserves border clipping) and try-fix-5's intersection approach. Code review findings produced meaningfully diverse and higher-quality alternatives.

Summary

PR #30408 correctly fixes a Windows-specific clip collision between ContentPanel.UpdateClip() and WrapperView when a ContentView with a user-set Clip is placed inside a Border. Gate passed. However, the expert reviewer (maui-expert-reviewer) found that the PR's guard is fragile: it relies on the SetupContainer() side-effect that leaves ContentPanel._content stale. Try-fix exploration produced a superior alternative (try-fix-1) that is simpler, more correct, and eliminates the fragility.

Root Cause

After SetupContainer() wraps the child in a WrapperView, ContentPanel._content is never updated to point to the WrapperView. Both ContentPanel.UpdateClip() and WrapperView.UpdateClip() then target the same composition visual. Since ContentPanel.SizeChanged fires after WrapperView.SizeChanged, ContentPanel's clip overwrites WrapperView's user-defined clip.

Fix Quality

PR's fix: A guard if (visual.Clip != null && Content.Parent is WrapperView) return; correctly identifies the collision state and bails out. It passes all tests. However, the guard is silently coupled to the stale _content reference — if SetupContainer() is ever fixed to update _content, the guard stops firing with no compiler or runtime warning. Also: the PR fix unconditionally skips ContentPanel's clip, meaning Border's StrokeShape clipping is also lost in this scenario (the border's rounded-corner clip is no longer applied to the wrapped content).

try-fix-1 (winner): Instead of skipping ContentPanel's clip, it redirects it to the WrapperView's composition visual via a private GetBorderClipTarget() helper. This gives two clips on two separate visual elements (no collision), preserves ContentPanel's border-shape clipping for StrokeShape scenarios, and does not rely on any stale reference side-effect. The condition Content.Parent is WrapperView wrapper && wrapper.Parent == this is positive and architectural, not a heuristic about stale state. It is the simplest passing alternative that is strictly more correct than the PR.

Ranking (passing candidates):

  1. try-fix-1 — Best: simplest, most correct, preserves border clipping, single file
  2. try-fix-3 — Root-cause fix is architecturally ideal but more invasive (handler lifecycle change, higher regression risk)
  3. pr-plus-reviewer — PR fix + reviewer improvements (adds retryTimeout, RoundRect test) — still uses the fragile guard
  4. try-fix-4 — Ownership contract flag: two files, explicit but adds API surface
  5. try-fix-5 — Intersection merge: correct but complex Win2D geometry math
  6. pr — Correct but fragile, silently loses StrokeShape clip in wrapped scenario
  7. try-fix-2 — Ordering fix: works but introduces single-frame flicker risk during animation
  8. try-fix-6 — Failed (rendering artifacts, IRoundRectangle interface limitation)

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

🤖 Automated review — alternative fix proposed

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

Why: try-fix-1 redirects ContentPanel's border clip to the WrapperView's composition visual (via GetBorderClipTarget()), so the border clip and WrapperView's user clip apply to separate visual elements with no collision. This is strictly better than the PR's guard: it preserves Border's StrokeShape clipping in the wrapped scenario, relies on a positive architectural condition rather than a stale-reference side-effect, and is the simplest single-file change among all passing candidates.

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

Candidate diff (`try-fix-1`)
diff --git a/src/Core/src/Platform/Windows/ContentPanel.cs b/src/Core/src/Platform/Windows/ContentPanel.cs
index 61c4e6677e..c9f9c319ef 100644
--- a/src/Core/src/Platform/Windows/ContentPanel.cs
+++ b/src/Core/src/Platform/Windows/ContentPanel.cs
@@ -181,7 +181,12 @@ namespace Microsoft.Maui.Platform
 				return;
 			}
 
-			var visual = ElementCompositionPreview.GetElementVisual(Content);
+			// When SetupContainer() wraps Content in a WrapperView, apply the border clip
+			// to the WrapperView's visual instead of Content's visual. This prevents the
+			// border clip from overwriting any clip that WrapperView manages on its child.
+			var clipTarget = GetBorderClipTarget();
+
+			var visual = ElementCompositionPreview.GetElementVisual(clipTarget);
 			var compositor = visual.Compositor;
 
 			PathF? clipPath;
@@ -206,10 +211,28 @@ namespace Microsoft.Maui.Platform
 			var pathGeometry = compositor.CreatePathGeometry(path);
 			var geometricClip = compositor.CreateGeometricClip(pathGeometry);
 
-			// The clip needs to consider the content's offset in case it is in a different position because of a different alignment.
-			geometricClip.Offset = new Vector2(strokeThickness - Content.ActualOffset.X, strokeThickness - Content.ActualOffset.Y);
+			// The clip needs to consider the clip target's offset in case it is in a different position because of a different alignment.
+			geometricClip.Offset = new Vector2(strokeThickness - clipTarget.ActualOffset.X, strokeThickness - clipTarget.ActualOffset.Y);
 
 			visual.Clip = geometricClip;
 		}
+
+		/// <summary>
+		/// Returns the element that should receive the border clip geometry.
+		/// If Content has been wrapped by a WrapperView (via SetupContainer), the
+		/// WrapperView is returned so the border clip and the WrapperView's own
+		/// child clip operate on separate visual elements.
+		/// </summary>
+		FrameworkElement GetBorderClipTarget()
+		{
+			// Content is guaranteed non-null here (UpdateClip guards with an early return).
+			// Use Parent == this instead of CachedChildren.Contains for O(1) lookup.
+			if (Content!.Parent is WrapperView wrapper && wrapper.Parent == this)
+			{
+				return wrapper;
+			}
+
+			return Content;
+		}
 	}
 }

@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels May 3, 2026
@kubaflo kubaflo changed the base branch from main to inflight/current May 4, 2026 14:43
@kubaflo kubaflo merged commit ef76e9c into dotnet:inflight/current May 4, 2026
119 of 130 checks passed
@github-actions github-actions Bot added this to the .NET 10 SR7 milestone May 4, 2026
github-actions Bot pushed a commit that referenced this pull request May 6, 2026
…ide the Border (#30408)

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

### Root Cause of the issue



- On Windows, when a ContentView with a clip applied during the
SizeChanged event is placed inside a Border, both the ContentView
(through WrapperView) and the Border's content (via ContentPanel)
attempt to apply their own clips to the same visual element.

- The clip set in the ContentPanel.UpdateClip method of the Border is
applied after the WrapperView has already set its clip, resulting in the
Border overwriting the clip geometry originally applied.



### Description of Change



- The fix introduces a condition in ContentPanel.UpdateClip that checks:
- Whether the content's visual already has a clip (visual.Clip is not
null)
- Whether the content is wrapped by a WrapperView (Content.Parent is
WrapperView)
- If both conditions are met, the ContentPanel skips applying its own
clip to the content, allowing the ContentView’s clip (set by the
WrapperView) to be preserved.



### Issues Fixed



Fixes #30404 



### Tested the behaviour in the following platforms



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



### Screenshot



| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img
src="https://github.com/user-attachments/assets/04381895-5d99-4ac8-9a2e-7d35aaa03dd4">
| <img
src="https://github.com/user-attachments/assets/04938f65-5c0a-4a4b-9676-c34c3ac30378">
|
PureWeen pushed a commit that referenced this pull request Jun 2, 2026
…ide the Border (#30408)

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

### Root Cause of the issue



- On Windows, when a ContentView with a clip applied during the
SizeChanged event is placed inside a Border, both the ContentView
(through WrapperView) and the Border's content (via ContentPanel)
attempt to apply their own clips to the same visual element.

- The clip set in the ContentPanel.UpdateClip method of the Border is
applied after the WrapperView has already set its clip, resulting in the
Border overwriting the clip geometry originally applied.



### Description of Change



- The fix introduces a condition in ContentPanel.UpdateClip that checks:
- Whether the content's visual already has a clip (visual.Clip is not
null)
- Whether the content is wrapped by a WrapperView (Content.Parent is
WrapperView)
- If both conditions are met, the ContentPanel skips applying its own
clip to the content, allowing the ContentView’s clip (set by the
WrapperView) to be preserved.



### Issues Fixed



Fixes #30404 



### Tested the behaviour in the following platforms



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



### Screenshot



| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img
src="https://github.com/user-attachments/assets/04381895-5d99-4ac8-9a2e-7d35aaa03dd4">
| <img
src="https://github.com/user-attachments/assets/04938f65-5c0a-4a4b-9676-c34c3ac30378">
|
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-border Border community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/windows s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows] ContentView clip is not updated when wrapping inside the Border

7 participants