[Windows] Fix for RadioButton BorderColor and BorderWidth not updated at runtime#28335
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
@jsuarezruiz , I have committed the pending snapshots. |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
jsuarezruiz
left a comment
There was a problem hiding this comment.
c7de3f2 to
aef8644
Compare
|
@jsuarezruiz , Previously, on Windows, if the BorderWidth was not explicitly specified, it would default to a Thickness object with a value of 3. As a result, the border would appear visible on Windows even though no border width was set, which could lead to inconsistencies in the UI across different platforms. Later it was fixed in this PR. In addition to setting the BorderColor, I have also included the BorderWidth in the test case to ensure consistent behavior across all platforms. This ensures that the border styling (both color and width) is applied uniformly, making the appearance of the RadioButton consistent regardless of the platform. |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
/rebase |
3527aee to
6e71922
Compare
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…cused visual state ).
…cused visual state ).
…cused visual state )
…cused visual state )
…cused visual state ).
…d remove baseline snapshots for Android, iOS, and Mac
…ropertyChanged override
9cea0cf to
8f19742
Compare
🚦 Gate — Test Before and After Fix
🚦 Gate Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue15806 Issue15806 |
✅ FAIL — 550s | ✅ PASS — 450s |
🔴 Without fix — 🖥️ Issue15806: FAIL ✅ · 550s
Determining projects to restore...
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 51.39 sec).
Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 51.61 sec).
Restored D:\a\1\s\src\Controls\Foldable\src\Controls.Foldable.csproj (in 396 ms).
Restored D:\a\1\s\src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 3.71 sec).
Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 24 ms).
Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 16 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 82 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 3.57 sec).
Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 19 ms).
Restored D:\a\1\s\src\Controls\tests\TestCases.HostApp\Controls.TestCases.HostApp.csproj (in 892 ms).
3 of 14 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
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.13859157
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.13859157
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.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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
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
Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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:05:38.82
Determining projects to restore...
Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils\VisualTestUtils.csproj (in 724 ms).
Restored D:\a\1\s\src\TestUtils\src\UITest.NUnit\UITest.NUnit.csproj (in 2.27 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\VisualTestUtils.MagickNet\VisualTestUtils.MagickNet.csproj (in 5.54 sec).
Restored D:\a\1\s\src\TestUtils\src\UITest.Appium\UITest.Appium.csproj (in 2.41 sec).
Restored D:\a\1\s\src\Controls\tests\TestCases.WinUI.Tests\Controls.TestCases.WinUI.Tests.csproj (in 4.96 sec).
Restored D:\a\1\s\src\Controls\tests\CustomAttributes\Controls.CustomAttributes.csproj (in 4 ms).
Restored D:\a\1\s\src\TestUtils\src\UITest.Analyzers\UITest.Analyzers.csproj (in 9.19 sec).
7 of 15 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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.13859157
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
>>>>> 4/16/2026 6:59:55 PM FixtureSetup for Issue15806(Windows)
>>>>> 4/16/2026 7:00:04 PM ValidateRadioButtonBorderColor Start
>>>>> 4/16/2026 7:00:07 PM ValidateRadioButtonBorderColor Stop
>>>>> 4/16/2026 7:00:07 PM Log types:
Failed ValidateRadioButtonBorderColor [3 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: ValidateRadioButtonBorderColor.png (0.86% 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.Issue15806.ValidateRadioButtonBorderColor() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue15806.cs:line 20
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.14] Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.43] Discovered: Controls.TestCases.WinUI.Tests
Total tests: 1
Failed: 1
Test Run Failed.
Total time: 30.9080 Seconds
🟢 With fix — 🖥️ Issue15806: PASS ✅ · 450s
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
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.13859157
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.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
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.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:05:28.60
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.70-ci+azdo.13859157
Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.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.13859157
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
VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.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
>>>>> 4/16/2026 7:07:29 PM FixtureSetup for Issue15806(Windows)
>>>>> 4/16/2026 7:07:37 PM ValidateRadioButtonBorderColor Start
>>>>> 4/16/2026 7:07:39 PM ValidateRadioButtonBorderColor Stop
Passed ValidateRadioButtonBorderColor [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.12] Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.38] Discovered: Controls.TestCases.WinUI.Tests
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 24.0138 Seconds
📁 Fix files reverted (8 files)
src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txtsrc/Controls/src/Core/RadioButton/RadioButton.cs
🤖 AI Summary
📊 Review Session —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #28335 | Override OnPropertyChanged in RadioButton.cs to call Handler?.UpdateValue(StrokeColor/StrokeThickness) when BorderColorProperty/BorderWidthProperty change |
✅ PASSED (Gate) | RadioButton.cs, 2 test files, 7 PublicAPI files |
Mirrors Button.cs pattern exactly |
🔬 Code Review — Deep Analysis
Code Review — PR #28335
Independent Assessment
What this changes: RadioButton.cs gains an OnPropertyChanged override that calls Handler?.UpdateValue(nameof(IRadioButton.StrokeColor)) when BorderColorProperty changes, and similarly for BorderWidth → StrokeThickness. Two UI test files are added for issue #15806, plus a Windows snapshot.
Inferred motivation: RadioButton implements IBorderElement and exposes BorderColor/BorderWidth as BindablePropertys, but the handler mapper keyed on IRadioButton.StrokeColor/StrokeThickness was never told to re-run when those bindable properties changed. So VSM setters that change BorderColor (e.g., when focus is gained/lost) were silently swallowed — the handler never received the update and the native border remained stale.
Reconciliation with PR Narrative
Author claims: Fix for BorderColor and BorderWidth not updating at runtime; validated on Windows only; models the approach after Button.cs (referenced explicitly in the PR).
Agreement: The author's diagnosis and approach are correct. The root cause matches the code, and the reference to Button.cs is accurate — the pattern is identical.
Minor disagreement: The PR title says [Windows], but the change is in cross-platform RadioButton.cs and runs on all platforms. On Android, MapStrokeColor has a real implementation; on iOS it's a no-op. The fix is broader than Windows, but the only observable visual effect is on Windows and Android (where handlers actually apply the stroke).
Findings
⚠️ Warning — CI showing overall maui-pr failures
Build Analysis and the overall maui-pr check are reporting fail. Individual pipeline steps appear to mostly pass, but the overall failure is unclear. The author should confirm whether these are pre-existing infra flakes or caused by this PR before merge.
⚠️ Warning — Unresolved review comment about test correctness
An earlier Copilot review thread claims the test conflates "Checked" and "Focused" state. The current diff shows "CommonStates" with "Normal" and "Focused" states — which is the correct VSM group. That prior comment appears stale/outdated, but the thread is still open and not marked resolved. The author should explicitly address it.
⚠️ Warning — Test exercises only Windows; the fix is cross-platform
The test is wrapped in:
#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYSTThe comment cites issue #28163 (Focused/Unfocused events not firing on non-Windows). Legitimate for "Focused" state, but the OnPropertyChanged bridge fires on all platforms whenever a VSM setter updates BorderColor — not just focus events. Android's MapStrokeColor does have a real implementation. There's no test coverage on Android for the non-focus case.
💡 Suggestion — using System.Runtime.CompilerServices; import is justified
The PR adds this import for [CallerMemberName]. This is correct — Button.cs uses the same attribute. No action needed.
💡 Suggestion — ~ prefix in PublicAPI.Unshipped.txt
All 7 platform files correctly add:
~override Microsoft.Maui.Controls.RadioButton.OnPropertyChanged(string propertyName = null) -> void
The ~ prefix is consistent with other ~override entries. The nullability difference (string vs string?) is expected because RadioButton.cs has #nullable disable. Low-priority but confirming via dotnet format analyzers is a good practice.
Devil's Advocate
"Is calling Handler?.UpdateValue in OnPropertyChanged duplicating work?"
BorderElement.BorderColorProperty has a propertyChanged callback: OnBorderColorPropertyChanged(Color, Color). RadioButton's implementation of that interface method is intentionally empty. No double-update risk; the two mechanisms are independent.
"Could calling UpdateValue during property change cause reentrancy?"
Handler?.UpdateValue dispatches to the mapper, which reads IRadioButton.StrokeColor (backed by the BindableProperty). No write occurs; no cycle.
"Is this fix sound on Android?"
Android's MapStrokeColor calls GetPlatformRadioButton(handler)?.UpdateStrokeColor(radioButton) — a real implementation. The fix has observable effect on Android for VSM or programmatic BorderColor change. iOS maps are no-ops, consistent with the platform's ControlTemplate-based rendering path.
"Could Button have regressed when its identical OnPropertyChanged was added?"
Button.cs has had this pattern for a long time without issue. RadioButton's handler mapper already registered StrokeColor/StrokeThickness keys — this PR just ensures those mapper entries are triggered at the right time.
Verdict: NEEDS_CHANGES
Confidence: medium
Errors: 0 | Warnings: 3 | Suggestions: 2
The core logic change (OnPropertyChanged override) is correct, minimal, and follows the established Button.cs pattern precisely. PublicAPI files are properly updated. The blockers are: (1) CI showing Build Analysis and overall maui-pr failures that must be explained/resolved, and (2) an unresolved Copilot review thread that should be explicitly addressed.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Add ["BorderColor"] = MapStrokeColor and ["BorderWidth"] = MapStrokeThickness alias entries to RadioButtonHandler.Mapper in RadioButtonHandler.cs |
✅ Pass | 1 file (+4 lines) | No new public API; handler-level fix; cleanest |
| 2 | try-fix (claude-sonnet-4.6) | Fill in IBorderElement.OnBorderColorPropertyChanged + replace BorderWidthProperty with new BindableProperty with propertyChanged lambda |
✅ Pass | 1 file | No OnPropertyChanged override; uses existing interface hook; potential breaking change for BorderWidthProperty reference |
| 3 | try-fix (gpt-5.3-codex) | Controls-layer remap in RadioButton.RemapForControls() adding ReplaceMapping for BorderColor/BorderWidth → stroke handlers |
✅ Pass | 1 file | Controls-layer redirect; more indirection than needed |
| 4 | try-fix (gpt-5.4) | Windows-specific INotifyPropertyChanged subscription in RadioButtonHandler.Windows.cs ConnectHandler/DisconnectHandler |
✅ Pass | 1 file (+27 lines) | Windows-only; doesn't address cross-platform; more complex |
| PR | PR #28335 | Override OnPropertyChanged in RadioButton.cs calling Handler?.UpdateValue(StrokeColor/StrokeThickness) |
✅ PASSED (Gate) | RadioButton.cs + 7 PublicAPI files + 2 test files |
Mirrors Button.cs pattern; adds public API surface for all 7 platforms |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | No | Design space exhausted — all architectural layers covered |
Exhausted: Yes
Selected Fix: Candidate #1 (mapper aliases in RadioButtonHandler.cs) — Better than PR's fix
Reasoning:
- Candidate [Draft] Readme WIP #1 is simpler (4 lines vs 13+ lines) and touches only 1 file
- No new public API surface — PR's fix requires updating all 7
PublicAPI.Unshipped.txtfiles with~override RadioButton.OnPropertyChanged - The handler mapper is the architecturally correct place: the root cause is a mapper registration gap (
"BorderColor"not mapped), and the fix corrects it at the source - All 4 independent candidates passed, confirming multiple valid approaches exist
- Candidate Update README.md #2 creates a new
BindablePropertyinstance (potential breaking change for code referencingRadioButton.BorderWidthProperty == BorderElement.BorderWidthProperty) - Candidates Third #3 and Aloha System.Maui! #4 add more indirection or are Windows-only
📋 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #15806, Windows RadioButton BorderColor/BorderWidth runtime update |
| Code Review | NEEDS_CHANGES (medium) | 0 errors, 3 warnings, 2 suggestions |
| Gate | ✅ PASSED | windows |
| Try-Fix | ✅ COMPLETE | 4 attempts, 4 passing |
| Report | ✅ COMPLETE |
Code Review Impact on Try-Fix
The code review noted that the PR's fix adds public API surface (~override OnPropertyChanged) across all 7 platforms unnecessarily — this directly motivated exploring handler-level alternatives. Attempt 1 (claude-opus-4.6) found a cleaner fix (4-line mapper alias) that avoids all PublicAPI changes. The warning about cross-platform coverage with Windows-only test influenced Attempt 4 (Windows-specific handler subscription), which also passed but was more complex. The failure-mode probes (null BorderColor handling, reentrancy) were confirmed safe by the code review's Devil's Advocate analysis and no changes were needed there.
Summary
PR #28335 fixes a real and verified bug: RadioButton.BorderColor and BorderWidth changes at runtime (e.g., from VisualStateManager setters) are silently swallowed because the handler mapper never receives the update notification. The fix is functionally correct and Gate-verified on Windows. However, a simpler alternative exists that avoids adding new public API surface across all 7 platforms.
The recommendation is REQUEST CHANGES to suggest the simpler mapper-alias approach — not because the PR's fix is wrong, but because a 4-line change in 1 file accomplishes the same goal with less churn.
Root Cause
RadioButton.BorderColorProperty and BorderWidthProperty are BindableProperty aliases to BorderElement's properties. When their values change (e.g., via VSM), Element.OnPropertyChanged fires with "BorderColor" / "BorderWidth" and dispatches to the handler mapper. However, the mapper only registered keys for "StrokeColor" / "StrokeThickness" (the IRadioButton interface names). The "BorderColor" and "BorderWidth" string keys had no mapper entries, so the notifications were dropped with no effect on the native view.
Fix Quality
PR's fix (override OnPropertyChanged in RadioButton.cs): Correct and follows the Button.cs precedent. However, it adds ~override RadioButton.OnPropertyChanged to all 7 PublicAPI.Unshipped.txt files, broadens the public API surface unnecessarily, and adds 13 lines of cross-platform code for what is fundamentally a mapper registration gap.
Better alternative (mapper alias entries in RadioButtonHandler.cs): Add 4 lines to RadioButtonHandler.Mapper:
["BorderColor"] = MapStrokeColor,
["BorderWidth"] = MapStrokeThickness,This fixes the registration gap at the source, requires no PublicAPI.Unshipped.txt changes, no new using directives, and is fully tested and verified. Verified: ✅ PASSED ValidateRadioButtonBorderColor on Windows.
Note: The test structure is valid — it uses CommonStates VSM group with Normal and Focused states, which is the correct structure. The prior Copilot review thread about test correctness appears stale (it referenced "CheckedStates" which is not in the current code). The author should close that thread. The #if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST guard is appropriate given issue #28163.
… at runtime (dotnet#28335) ### Issue Details - The radio button's BorderColor and BorderWidth are not updated at runtime. ### Root Cause - Handling of runtime changes for BorderColor and BorderWidth has not been implemented. ### Description of Change - Implemented the OnPropertyChanged method to check if the changed property is either BorderColor or BorderWidth. If so, then updated the StrokeColor or StrokeThickness of the radio button in the handler accordingly. ### Reference : https://github.com/dotnet/maui/blob/ac01bde86e5105c1ca9c121c68c2a068a9f3ef27/src/Controls/src/Core/Button/Button.cs#L472 ### Validated the behaviour in the following platforms - [x] Windows - [ ] Android - [ ] iOS - [ ] Mac ### Issues Fixed Fixes dotnet#15806 ### Output | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/3476b4aa-6670-4d6a-b471-506b42b3e9c3"> | <video src="https://github.com/user-attachments/assets/880b40ca-ddda-4174-b621-d74dbb5853c4"> | --------- Co-authored-by: Jakub Florkowski <[email protected]>
… at runtime (#28335) ### Issue Details - The radio button's BorderColor and BorderWidth are not updated at runtime. ### Root Cause - Handling of runtime changes for BorderColor and BorderWidth has not been implemented. ### Description of Change - Implemented the OnPropertyChanged method to check if the changed property is either BorderColor or BorderWidth. If so, then updated the StrokeColor or StrokeThickness of the radio button in the handler accordingly. ### Reference : https://github.com/dotnet/maui/blob/ac01bde86e5105c1ca9c121c68c2a068a9f3ef27/src/Controls/src/Core/Button/Button.cs#L472 ### Validated the behaviour in the following platforms - [x] Windows - [ ] Android - [ ] iOS - [ ] Mac ### Issues Fixed Fixes #15806 ### Output | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/3476b4aa-6670-4d6a-b471-506b42b3e9c3"> | <video src="https://github.com/user-attachments/assets/880b40ca-ddda-4174-b621-d74dbb5853c4"> | --------- Co-authored-by: Jakub Florkowski <[email protected]>
… at runtime (#28335) ### Issue Details - The radio button's BorderColor and BorderWidth are not updated at runtime. ### Root Cause - Handling of runtime changes for BorderColor and BorderWidth has not been implemented. ### Description of Change - Implemented the OnPropertyChanged method to check if the changed property is either BorderColor or BorderWidth. If so, then updated the StrokeColor or StrokeThickness of the radio button in the handler accordingly. ### Reference : https://github.com/dotnet/maui/blob/ac01bde86e5105c1ca9c121c68c2a068a9f3ef27/src/Controls/src/Core/Button/Button.cs#L472 ### Validated the behaviour in the following platforms - [x] Windows - [ ] Android - [ ] iOS - [ ] Mac ### Issues Fixed Fixes #15806 ### Output | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/3476b4aa-6670-4d6a-b471-506b42b3e9c3"> | <video src="https://github.com/user-attachments/assets/880b40ca-ddda-4174-b621-d74dbb5853c4"> | --------- Co-authored-by: Jakub Florkowski <[email protected]>
… at runtime (#28335) ### Issue Details - The radio button's BorderColor and BorderWidth are not updated at runtime. ### Root Cause - Handling of runtime changes for BorderColor and BorderWidth has not been implemented. ### Description of Change - Implemented the OnPropertyChanged method to check if the changed property is either BorderColor or BorderWidth. If so, then updated the StrokeColor or StrokeThickness of the radio button in the handler accordingly. ### Reference : https://github.com/dotnet/maui/blob/ac01bde86e5105c1ca9c121c68c2a068a9f3ef27/src/Controls/src/Core/Button/Button.cs#L472 ### Validated the behaviour in the following platforms - [x] Windows - [ ] Android - [ ] iOS - [ ] Mac ### Issues Fixed Fixes #15806 ### Output | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/3476b4aa-6670-4d6a-b471-506b42b3e9c3"> | <video src="https://github.com/user-attachments/assets/880b40ca-ddda-4174-b621-d74dbb5853c4"> | --------- Co-authored-by: Jakub Florkowski <[email protected]>
… at runtime (#28335) ### Issue Details - The radio button's BorderColor and BorderWidth are not updated at runtime. ### Root Cause - Handling of runtime changes for BorderColor and BorderWidth has not been implemented. ### Description of Change - Implemented the OnPropertyChanged method to check if the changed property is either BorderColor or BorderWidth. If so, then updated the StrokeColor or StrokeThickness of the radio button in the handler accordingly. ### Reference : https://github.com/dotnet/maui/blob/ac01bde86e5105c1ca9c121c68c2a068a9f3ef27/src/Controls/src/Core/Button/Button.cs#L472 ### Validated the behaviour in the following platforms - [x] Windows - [ ] Android - [ ] iOS - [ ] Mac ### Issues Fixed Fixes #15806 ### Output | Before Fix | After Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/3476b4aa-6670-4d6a-b471-506b42b3e9c3"> | <video src="https://github.com/user-attachments/assets/880b40ca-ddda-4174-b621-d74dbb5853c4"> | --------- Co-authored-by: Jakub Florkowski <[email protected]>



Issue Details
Root Cause
Description of Change
Reference :
maui/src/Controls/src/Core/Button/Button.cs
Line 472 in ac01bde
Validated the behaviour in the following platforms
Issues Fixed
Fixes #15806
Output
RadioButtonBorderColorNotChanged.mp4
RadioButtonBorderColorChanged.mp4