Skip to content

[iOS] Fix RadioButton BackgroundColor bleeding outside CornerRadius#34844

Open
SyedAbdulAzeemSF4852 wants to merge 5 commits intodotnet:mainfrom
SyedAbdulAzeemSF4852:fix/radiobutton-background-bleed-ios
Open

[iOS] Fix RadioButton BackgroundColor bleeding outside CornerRadius#34844
SyedAbdulAzeemSF4852 wants to merge 5 commits intodotnet:mainfrom
SyedAbdulAzeemSF4852:fix/radiobutton-background-bleed-ios

Conversation

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

Issue Details

  • On iOS, when a RadioButton has a BackgroundColor and CornerRadius set, the background color extends beyond the rounded corners as a full rectangle.

Root Cause

  • On iOS, the RadioButton's BackgroundColor is applied by ViewHandler.MapBackground directly to the outer handler platform view (ContentView/UIView), which has no corner radius or clipping. The rounded corners and border stroke are rendered by a separate inner Border element inside the ControlTemplate, so the background color paints as a full rectangle behind the rounded border, visibly bleeding outside the corners.

Description of Change

  • Updated the RadioButton control template to bind the Border.BackgroundColorProperty to the RadioButton.BackgroundColor, ensuring the background is rendered inside the clipped border area (RadioButton.cs).
  • Removed the background color binding from the ContentPresenter, preventing it from affecting the outer area (RadioButton.cs).
  • On iOS and MacCatalyst, suppressed the default handler's MapBackground method to prevent the platform view's background from bleeding outside the border (RadioButtonHandler.iOS.cs).
  • Declared the new MapBackground method in the public API files for iOS and MacCatalyst (PublicAPI.Unshipped.txt).

Issues Fixed

Fixes #34842

Validated the behaviour in the following platforms

  • Windows
  • Android
  • iOS
  • Mac

Output

Before After

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 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 -- 34844

Or

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

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Apr 7, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Hey there @@SyedAbdulAzeemSF4852! 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 Apr 7, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review April 7, 2026 14:26
Copilot AI review requested due to automatic review settings April 7, 2026 14:26
@sheiksyedm sheiksyedm added platform/ios area-controls-radiobutton RadioButton, RadioButtonGroup labels Apr 7, 2026
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

Fixes iOS/MacCatalyst RadioButton background rendering so BackgroundColor respects CornerRadius and doesn’t “bleed” outside rounded corners by moving background rendering into the templated Border and suppressing handler background mapping on iOS/MacCatalyst. Adds a regression UI test and platform baselines.

Changes:

  • Updated the default RadioButton ControlTemplate to apply BackgroundColor to the Border and removed background binding from the ContentPresenter.
  • Overrode RadioButtonHandler.MapBackground on iOS/MacCatalyst as a no-op to prevent the outer platform view background from painting outside rounded corners; updated handler mapper conditionals.
  • Added Issue #34842 HostApp page + UI test + Android/iOS snapshot baselines; updated iOS/MacCatalyst PublicAPI unshipped files.

Reviewed changes

Copilot reviewed 7 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Declares new RadioButtonHandler.MapBackground API for MacCatalyst.
src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Declares new RadioButtonHandler.MapBackground API for iOS.
src/Core/src/Handlers/RadioButton/RadioButtonHandler.iOS.cs Adds iOS/MacCatalyst no-op MapBackground to suppress platform background painting.
src/Core/src/Handlers/RadioButton/RadioButtonHandler.cs Changes mapper to map Background for all non-Tizen targets.
src/Controls/src/Core/RadioButton/RadioButton.cs Binds template Border.BackgroundColor to RadioButton.BackgroundColor; removes ContentPresenter background binding.
src/Controls/src/Core/RadioButton/RadioButton.iOS.cs Minor formatting/bracing change.
src/Controls/tests/TestCases.HostApp/Issues/Issue34842.cs New HostApp repro page for the issue.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34842.cs New UI test verifying clipped background with rounded corners.
src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButtonBackgroundColorClippedToCornerRadius.png iOS baseline for new UI screenshot test.
src/Controls/tests/TestCases.Android.Tests/snapshots/android/RadioButtonBackgroundColorClippedToCornerRadius.png Android baseline for new UI screenshot test.

Comment thread src/Core/src/Handlers/RadioButton/RadioButtonHandler.iOS.cs
Comment thread src/Controls/src/Core/RadioButton/RadioButton.cs
Comment thread src/Controls/tests/TestCases.HostApp/Issues/Issue34842.cs
@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@MauiBot MauiBot added s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Apr 16, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 16, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 16, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 16, 2026

🚦 Gate — Test Before and After Fix

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

🚦 Gate Sessionfdd8051 · Update RadioButton template to bind Border.Background for brush support and add baseline snapshots · 2026-04-16 19:05 UTC

Gate Result: ✅ PASSED

Platform: IOS · Base: main · Merge base: eb0b82fe

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue34842 Issue34842 ✅ FAIL — 213s ✅ PASS — 96s
🔴 Without fix — 🖥️ Issue34842: FAIL ✅ · 213s
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 521 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 645 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 10.11 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 17.8 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 17.8 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 17.81 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 17.81 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/maps/src/Maps.csproj (in 17.82 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 17.81 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 17.82 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 17.83 sec).
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Microsoft.AspNetCore.Components.WebView.Maui -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-ios26.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
  Detected signing identity:
    Code Signing Key: "" (-)
    Provisioning Profile: "" () - no entitlements
    Bundle Id: com.microsoft.maui.uitests
    App Id: com.microsoft.maui.uitests
  Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
  Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
  Optimizing assemblies for size. This process might take a while.

Build succeeded.

/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
    1 Warning(s)
    0 Error(s)

Time Elapsed 00:01:38.88
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 564 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 564 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 564 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 564 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 564 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 0.9 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 591 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 608 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 1.33 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.86 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.41 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 2.72 sec).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.iOS.Tests/Controls.TestCases.iOS.Tests.csproj (in 2.14 sec).
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
[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.05]   Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.14]   Discovered:  Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 4/16/2026 12:03:57 PM FixtureSetup for Issue34842(iOS)
>>>>> 4/16/2026 12:04:01 PM RadioButtonBackgroundColorClippedToCornerRadius Start
>>>>> 4/16/2026 12:04:03 PM RadioButtonBackgroundColorClippedToCornerRadius Stop
>>>>> 4/16/2026 12:04:03 PM Log types: syslog, crashlog, performance, safariConsole, safariNetwork, server
  Failed RadioButtonBackgroundColorClippedToCornerRadius [1 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: RadioButtonBackgroundColorClippedToCornerRadius.png (0.67% 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) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
   at Microsoft.Maui.TestCases.Tests.Issues.Issue34842.RadioButtonBackgroundColorClippedToCornerRadius() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34842.cs:line 18
   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

Test Run Failed.
Total tests: 1
     Failed: 1
 Total time: 1.0086 Minutes

🟢 With fix — 🖥️ Issue34842: PASS ✅ · 96s
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 347 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 356 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 361 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 397 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 408 ms).
  6 of 11 projects are up-to-date for restore.
/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0-ios26.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0-ios26.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0-ios26.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Microsoft.AspNetCore.Components.WebView.Maui -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-ios26.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Maps -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Foldable -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-ios26.0/Microsoft.Maui.Controls.Foldable.dll
  Detected signing identity:
    Code Signing Key: "" (-)
    Provisioning Profile: "" () - no entitlements
    Bundle Id: com.microsoft.maui.uitests
    App Id: com.microsoft.maui.uitests
  Controls.TestCases.HostApp -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-ios/iossimulator-arm64/Controls.TestCases.HostApp.dll
  Optimizing assemblies for size may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
  Optimizing assemblies for size. This process might take a while.

Build succeeded.

/Users/cloudtest/vss/_work/1/s/.dotnet/packs/Microsoft.iOS.Sdk.net10.0_26.0/26.0.11017/targets/Xamarin.Shared.Sdk.targets(309,3): warning : RuntimeIdentifier was set on the command line, and will override the value for RuntimeIdentifiers set in the project file. [/Users/cloudtest/vss/_work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-ios]
    1 Warning(s)
    0 Error(s)

Time Elapsed 00:00:46.50
  Determining projects to restore...
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 371 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Essentials/src/Essentials.csproj (in 373 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 372 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 399 ms).
  Restored /Users/cloudtest/vss/_work/1/s/src/Core/src/Core.csproj (in 380 ms).
  8 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Controls.CustomAttributes -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  Graphics -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Essentials -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859302
  Controls.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  VisualTestUtils -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.Core -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils.MagickNet -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Appium -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.NUnit -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  UITest.Analyzers -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.iOS.Tests -> /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
Test run for /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (arm64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
[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.06]   Discovering: Controls.TestCases.iOS.Tests
[xUnit.net 00:00:00.15]   Discovered:  Controls.TestCases.iOS.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /Users/cloudtest/vss/_work/1/s/artifacts/bin/Controls.TestCases.iOS.Tests/Debug/net10.0/Controls.TestCases.iOS.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 4/16/2026 12:05:34 PM FixtureSetup for Issue34842(iOS)
>>>>> 4/16/2026 12:05:38 PM RadioButtonBackgroundColorClippedToCornerRadius Start
>>>>> 4/16/2026 12:05:38 PM RadioButtonBackgroundColorClippedToCornerRadius Stop
  Passed RadioButtonBackgroundColorClippedToCornerRadius [462 ms]
NUnit Adapter 4.5.0.0: Test execution complete

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

📁 Fix files reverted (5 files)
  • src/Controls/src/Core/RadioButton/RadioButton.cs
  • src/Core/src/Handlers/RadioButton/RadioButtonHandler.cs
  • src/Core/src/Handlers/RadioButton/RadioButtonHandler.iOS.cs
  • src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt
  • src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 16, 2026

🤖 AI Summary

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

📊 Review Sessionfdd8051 · Update RadioButton template to bind Border.Background for brush support and add baseline snapshots · 2026-04-16 20:51 UTC
🔍 Pre-Flight — Context & Validation

Issue: #34842 - [iOS] RadioButton BackgroundColor bleeds outside CornerRadius
PR: #34844 - [iOS] Fix RadioButton BackgroundColor bleeding outside CornerRadius
Platforms Affected: iOS (primary), macCatalyst (secondary)
Files Changed: 3 implementation, 3 test (+ snapshot images)

Key Findings

  • Root cause: ViewHandler.MapBackground paints background on the outer ContentView (UIView), which has no corner-radius clipping. The rounded border lives inside the ControlTemplate's inner Border element.
  • Fix: Bind Background and BackgroundColor directly on the template Border; suppress outer handler MapBackground with a no-op on iOS/macCatalyst.
  • Prior Copilot review suggested adding Border.BackgroundProperty binding (for brush support). Author addressed this.
  • MapBackground conditional changed from #if ANDROID || WINDOWS to #if !TIZEN — broadens scope to include iOS/macCatalyst explicitly in the mapper (where the no-op override takes effect).
  • contentPresenter.SetBinding(BackgroundColorProperty, ...) was removed — correct; background now belongs to the Border.
  • All CI checks pass (green across all build/integration/helix jobs).

Code Review Summary

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

Key code review findings:

  • ⚠️ RadioButtonHandler.iOS.cs:69-73 — Custom ControlTemplate users on iOS/macCatalyst will lose background rendering; the no-op suppresses the base handler for ALL RadioButton instances, not just default-template ones
  • ⚠️ RadioButton.cs:515-516 — Template change is cross-platform but Windows/Android are unvalidated by PR author; CI confirms inert on those platforms
  • 💡 RadioButton.cs:515-516 — Dual BackgroundColor + Background binding is correct but non-obvious; a comment explaining the split (solid vs brush) would help future reviewers

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34844 Bind background to template Border + no-op MapBackground on iOS ✅ PASSED (Gate) RadioButtonHandler.cs, RadioButtonHandler.iOS.cs, RadioButton.cs Original PR

🔬 Code Review — Deep Analysis

Code Review — PR #34844

Independent Assessment

What this changes: Three coordinated modifications: (1) The RadioButton.BuildDefaultTemplate() gains two new bindings on its Border element — BackgroundColorProperty and BackgroundProperty — both bound to the templated parent's corresponding properties. Simultaneously, the existing BackgroundColorProperty binding that was on the inner ContentPresenter is removed. (2) RadioButtonHandler.cs expands its MapBackground mapper entry from #if ANDROID || WINDOWS to #if !TIZEN, adding iOS/macCatalyst to the mapper. (3) RadioButtonHandler.iOS.cs adds a new no-op MapBackground static method (non-[MissingMapper]) that suppresses the base ViewHandler.MapBackground.

Inferred motivation: On iOS/macCatalyst, RadioButton uses ContentView (a UIView wrapper) as its platform view. The base ViewHandler.MapBackground paints background directly on this outer UIView, which has no corner-radius clipping. The visible border/corner radius lives inside an inner Border element within the default ControlTemplate. Result: the background paints as a full rectangle behind the rounded border, bleeding visibly outside the corners.


Reconciliation with PR Narrative

Author claims: Background bleeds outside CornerRadius on iOS because ViewHandler.MapBackground applies to the outer ContentView platform view, not the inner template Border. Fix is to bind background directly to the Border, suppress the outer handler, and add snapshot tests.

Agreement: ✅ Full agreement. The root cause analysis is accurate. ViewHandler.ViewMapper confirms [nameof(IView.Background)] = MapBackground is in the base mapper and would have painted the outer UIView. The fix correctly intercepts this.


Findings

⚠️ Warning — Custom ControlTemplate background silently broken on iOS/macCatalyst

File: src/Core/src/Handlers/RadioButton/RadioButtonHandler.iOS.cs:69-73 and RadioButtonHandler.cs:19-21

The new iOS no-op MapBackground suppresses the base ViewHandler.MapBackground for all RadioButton instances on iOS/macCatalyst — not just those using the default template. Any user who supplies a custom ControlTemplate and relies on the outer platform view receiving the BackgroundColor will now see no background rendered at all on iOS/macCatalyst.

Before: Colors.Blue applied to outer UIView (wrong — bled outside corners, but visible).
After: nothing — the no-op fires, the custom template's tree doesn't bind BackgroundColor unless the user explicitly added that binding.

The old behaviour was wrong (bleeding), but the change is silent and undocumented. A comment alongside the new override noting the behaviour change for custom-template users would help. Example:

// NOTE: This suppresses background from being applied to the outer platform view.
// Users with custom ControlTemplates on iOS/macCatalyst must bind Background/BackgroundColor
// within the template explicitly.
public static void MapBackground(IRadioButtonHandler handler, IRadioButton radioButton) { }

Whether this behaviour change is acceptable as-is is a maintainer judgment call.


⚠️ Warning — Windows and Android manually unvalidated while template is cross-platform

File: src/Controls/src/Core/RadioButton/RadioButton.cs:515-516

The two new Border bindings live in BuildDefaultTemplate() which is compiled for all platforms. On Android (AppCompatRadioButton) and Windows (WinUI RadioButton), the native handler uses the native platform widget directly — the MAUI ControlTemplate is not used — so the template bindings are effectively inert on those platforms. However, the PR description explicitly marks Windows and Android as not manually validated.

This is low risk given that CI snapshot tests pass on all four platforms and the template path is confirmed inactive on Android/Windows. Still worth a note in case the template-vs-native split changes in the future.


💡 Suggestion — The dual BackgroundColor + Background binding is non-obvious; a comment would help

File: src/Controls/src/Core/RadioButton/RadioButton.cs:515-516

Binding both Border.BackgroundColorProperty and Border.BackgroundProperty is correct. In VisualElement, BackgroundColor and Background are independent BindableProperty fields. The IView.Background getter combines them (Background takes precedence; BackgroundColor is the fallback). Binding both to the Border correctly covers the solid-color case (BackgroundColor only) and the gradient brush case (Background only) without either binding accidentally clearing the other.

A brief comment would save the next reviewer from having to re-derive this.


Devil's Advocate

Challenging the custom-ControlTemplate concern: Custom template users are expected to own all property bindings in their template. The old outer-UIView background was an undocumented implementation detail, not a supported contract. The MAUI pattern for templated controls is that the template, not the handler, handles rendering — so the new behaviour is arguably more correct. This weakens the warning.

Challenging approval: The #if !TIZEN guard is correct, but deserves a second look — changing from an explicit allowlist (ANDROID || WINDOWS) to an exclusion (!TIZEN) is a broader gate. The only currently-affected net-new platforms are iOS and macCatalyst, which is intentional, but any new platform added in the future (not Tizen) would also inherit the no-op MapBackground by default.

Challenging the test: The test is a VerifyScreenshot() without a retryTimeout, which is fine for a static control with no animations. The test correctly asserts visual correctness and includes baselines for all four platforms.


CI Status

All required checks pass (✅ maui-pr, all build/integration/helix jobs green, license/cla pass). No concerns.


Verdict: NEEDS_DISCUSSION

Confidence: medium

Summary: The core fix is technically sound — the root cause analysis is accurate, the mechanism (override MapBackground with a no-op + bind background in the template Border) is correct, and CI is fully green with cross-platform snapshot coverage. The open question for maintainers is whether the silent behavior change on iOS/macCatalyst for custom-ControlTemplate users (background no longer applied to the outer platform view) is acceptable without any documentation or guarding. If it is, this is effectively an LGTM pending a comment addition. If not, a note in the MapBackground docblock is the minimum required change.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Platform-level CALayer + on outer UIView; base MapBackground still active clipping PASS RadioButtonHandler.cs, RadioButtonHandler.iOS.cs, PublicAPI files Works for custom ControlTemplates; trade-off: ClipsToBounds clips overflow shadows
2 try-fix (claude-sonnet-4.6) Controls-layer ReplaceMapping: imperative push of RadioButton.Background to template Border; outer view FAIL RadioButton.cs, RadioButton.iOS.cs, RadioButton.Mapper.cs, PublicAPI files Functionally correct; failed only due to screenshot status-bar clock drift (0.67% visual diff) cleared
3 try-fix (gpt-5.3-codex) iOS MapBackground keeps normal background + applies without ClipsToBounds when CornerRadius > 0 PASS RadioButtonHandler.cs, RadioButtonHandler.iOS.cs Avoids ClipsToBounds trade-off; corner clip via CALayer
4 try-fix (gpt-5.4) Default template detection: clear outer ContentView bg only for default template; custom templates fall back to base MapBackground PASS RadioButton.cs, RadioButtonHandler.cs, RadioButtonHandler.iOS.cs Explicitly addresses custom ControlTemplate concern
PR PR #34844 Bind + in default template; no-op MapBackground on iOS/macCatalyst; remove ContentPresenter binding PASSED (Gate) RadioButtonHandler.cs, RadioButtonHandler.iOS.cs, RadioButton.cs Original PR

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 Yes VisualElement.Clip with RoundRectangleGeometry matching clips entire platform view via CAShapeLayer mask
claude-sonnet-4.6 2 Yes Dedicated CALayer sublayer at index 0 with CornerRadius + MasksToBounds; uses existing MAUI InsertBackgroundLayer infrastructure
gpt-5.3-codex 2 Yes CAShapeLayer as Layer.Mask from resolved RoundRectangle; update mask on layout/size changes
gpt-5.4 3 NO NEW IDEAS Exhausted after reviewing all 7+ approaches

Cross-poll Round 2 ideas were all more complex than existing passing candidates and not pursued.

Exhausted: Yes
Selected Fix: PR's declarative template-binding approach is architecturally cleanest (background belongs in the clipping Border element, not the outer UIView), fewest moving parts, most consistent with MAUI's templated control patterns. Attempt 4 is a noteworthy alternative if the custom ControlTemplate concern needs explicit handling.fix


📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE iOS-specific background bleed bug in RadioButton templated control
Code Review NEEDS_DISCUSSION (medium) 0 errors, 2 warnings (custom template silent behavior change; unvalidated platforms)
Gate ✅ PASSED ios
Try-Fix ✅ COMPLETE 4 attempts, 3 passing (Attempts 1, 3, 4 pass; Attempt 2 fails on screenshot clock drift only)
Report ✅ COMPLETE

Code Review Impact on Try-Fix

The code review warning about custom ControlTemplate users losing background rendering on iOS/macCatalyst was the primary concern taken into Try-Fix. This drove Attempt 4 (gpt-5.4), which explicitly distinguished between the default template and custom templates — clearing the outer view only when the default template is in use, leaving custom templates on the base MapBackground path.

The failure-mode probe ("custom ControlTemplate without explicit background binding → background disappears") was validated by exploration: Attempt 1 (CALayer + ClipsToBounds) and Attempt 3 (Layer.CornerRadius) both avoid this by leaving MapBackground active, while Attempt 4 conditionally handles it. The PR's fix is a conscious architectural choice that the default MAUI pattern is for templates to own their visual tree — a position the code review acknowledged as defensible.

Summary

PR #34844 fixes a real visual bug on iOS/macCatalyst where the RadioButton background color bleeds outside rounded corners. The fix is architecturally sound, CI is fully green, and 3 independent try-fix approaches all confirm the root cause and fix mechanism are correct. The code review raised a valid design question (custom ControlTemplate regression) that is a known trade-off rather than a blocking error. The PR's fix is the simplest and most idiomatic of all passing candidates.

Root Cause

ViewHandler.MapBackground in the base mapper paints the background directly onto the outer ContentView (UIView) platform view, which has no corner-radius clipping. RadioButton's ControlTemplate wraps its visible content (including the Border that defines the rounded shape) inside this outer platform view. The result: the background paints as a full rectangle behind the rounded border, visibly bleeding outside the corners.

Fix Quality

The PR's fix is high quality:

  • Correct mechanism: Background is redirected to the Border element inside the ControlTemplate which already clips its fill to the StrokeShape (rounded rectangle). This is where the background logically belongs.
  • Both BackgroundColor and Background are bound — covers solid color and gradient brush scenarios independently (both are separate BindableProperty fields on VisualElement).
  • ContentPresenter binding removed — eliminates double-rendering path that was also incorrect.
  • MapBackground no-op prevents the outer handler from re-applying background on top of the template-rendered one. The change from #if ANDROID || WINDOWS to #if !TIZEN correctly includes iOS/macCatalyst in the mapper while the iOS partial class override suppresses the behavior.
  • All CI green — cross-platform snapshot tests pass on iOS, Android, Windows, macCatalyst.
  • Prior Copilot review feedback addressed — brush-based background support added via Border.BackgroundProperty binding.

The one open discussion point (silent behavior change for custom ControlTemplate users on iOS/macCatalyst) is a known trade-off consistent with MAUI's template ownership model. A comment in RadioButtonHandler.iOS.cs documenting this would be helpful, but is not a blocker.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) labels Apr 16, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 18, 2026

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 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.

Could you please verify the failing tests?

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

Labels

area-controls-radiobutton RadioButton, RadioButtonGroup community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/ios s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates 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.

[iOS] RadioButton BackgroundColor bleeds outside CornerRadius

6 participants