Fix RadioButtonGroup not working with ContentView#34781
Fix RadioButtonGroup not working with ContentView#34781kubaflo merged 4 commits intodotnet:inflight/currentfrom
Conversation
Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://github.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34781Or
iex "& { $(irm https://github.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34781" |
🧪 PR Test EvaluationOverall Verdict: The unit test covers the primary regression scenario well, but it lacks explicit assertions for the layout's
📊 Expand Full EvaluationPR Test Evaluation ReportPR: #34781 — Fix RadioButtonGroup not working with ContentView Overall VerdictThe new unit test adequately covers the core regression (RadioButtons inside ContentView ControlTemplates not mutually excluding each other), but it only implicitly covers the null 1. Fix Coverage —
|
There was a problem hiding this comment.
Pull request overview
Fixes a .NET 10 regression where RadioButtonGroup fails to enforce single-selection when RadioButtons are hosted inside a ContentView control template (often before the visual tree is attached to a Page), and prevents unintended auto-checking when both SelectedValue and RadioButton.Value are null.
Changes:
- Prevent auto-checking newly discovered
RadioButtons when the group’sSelectedValueisnull. - Improve uncheck scope resolution by falling back to the group controller’s layout when no
Pagevisual root exists. - Add a unit test covering the
ContentView+ inlineControlTemplatescenario to prevent regressions.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Controls/src/Core/RadioButton/RadioButtonGroupController.cs | Guards the initial equality-based auto-check behavior and exposes the controller’s layout for scope resolution. |
| src/Controls/src/Core/RadioButton/RadioButtonGroup.cs | Uses the controller’s layout as the fallback uncheck root when no Page ancestor exists. |
| src/Controls/tests/Core.UnitTests/RadioButtonTests.cs | Adds a regression test reproducing the ContentView control-template initialization scenario. |
…ons and positive auto-check path
|
I have updated the unit test to include explicit assertions for the layout's SelectedValue property and added coverage for the positive auto-check path through the ContentView scenario. |
🚦 Gate — Test Before and After Fix
🚦 Gate Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🧪 RadioButtonTests RadioButtonTests |
✅ FAIL — 118s | ✅ PASS — 82s |
🔴 Without fix — 🧪 RadioButtonTests: FAIL ✅ · 118s
Determining projects to restore...
Restored /home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj (in 4.73 sec).
Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 2.71 sec).
Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 8.29 sec).
Restored /home/vsts/work/1/s/src/TestUtils/src/TestUtils/TestUtils.csproj (in 6 ms).
Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 11 ms).
Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 65 ms).
Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 33 ms).
Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 50 ms).
Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 85 ms).
1 of 10 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.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.
[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: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.39] Discovered: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.40] Starting: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.61] RadioButtonGroupWorksWithContentViewControlTemplate [FAIL]
[xUnit.net 00:00:01.61] Assert.False() Failure
[xUnit.net 00:00:01.61] Expected: False
[xUnit.net 00:00:01.61] Actual: True
[xUnit.net 00:00:01.61] Stack Trace:
[xUnit.net 00:00:01.61] /_/src/Controls/tests/Core.UnitTests/RadioButtonTests.cs(455,0): at Microsoft.Maui.Controls.Core.UnitTests.RadioButtonTests.RadioButtonGroupWorksWithContentViewControlTemplate()
[xUnit.net 00:00:01.61] at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
[xUnit.net 00:00:01.61] at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Passed RadioButtonGroupLayoutShouldNotLeak [94 ms]
Passed GroupControllerSelectionIsNullWhenSelectedButtonRemoved [13 ms]
Passed RadioButtonShouldNotLeak [19 ms]
Passed LayoutGroupNameAppliesToExistingRadioButtons [< 1 ms]
Passed GroupSelectedValueUpdatesWhenSelectedButtonValueUpdates [< 1 ms]
Passed GroupNullSelectionClearsAnySelection [< 1 ms]
Passed ValuePropertyCanBeSetToNull [< 1 ms]
Passed ImpliedGroupDoesNotIncludeExplicitGroups [< 1 ms]
Failed RadioButtonGroupWorksWithContentViewControlTemplate [5 ms]
Error Message:
Assert.False() Failure
Expected: False
Actual: True
Stack Trace:
at Microsoft.Maui.Controls.Core.UnitTests.RadioButtonTests.RadioButtonGroupWorksWithContentViewControlTemplate() in /_/src/Controls/tests/Core.UnitTests/RadioButtonTests.cs:line 455
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Passed RadioButtonAddedToGroupGetsGroupName [< 1 ms]
Passed RadioButtonGroupWorksWithDynamicallyAddedDescendants [1 ms]
Passed RadioButtonGroupSelectedValueBindingWorksWithNestedDescendants [< 1 ms]
Passed ImpliedGroup [< 1 ms]
Passed ThereCanBeOnlyOne [< 1 ms]
Passed NestedRadioButtonAddedToGroupGetsGroupName [< 1 ms]
Passed UpdatedGroupNameAppliesToRadioButtonsWithOldGroupName [< 1 ms]
Passed RadioButtonAddedToGroupKeepsGroupName [< 1 ms]
Passed RemovingSelectedButtonFromGroupClearsSelection [< 1 ms]
[xUnit.net 00:00:01.63] Finished: Microsoft.Maui.Controls.Core.UnitTests
Passed RadioButtonGroupAutoChecksMatchingButtonInContentViewWhenSelectedValuePreset [< 1 ms]
Test Run Failed.
Total tests: 19
Passed: 18
Failed: 1
Total time: 2.1226 Seconds
🟢 With fix — 🧪 RadioButtonTests: PASS ✅ · 82s
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13810030
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.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.
[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: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.41] Discovered: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.42] Starting: Microsoft.Maui.Controls.Core.UnitTests
Passed RadioButtonGroupLayoutShouldNotLeak [79 ms]
Passed GroupControllerSelectionIsNullWhenSelectedButtonRemoved [12 ms]
Passed RadioButtonShouldNotLeak [18 ms]
Passed LayoutGroupNameAppliesToExistingRadioButtons [< 1 ms]
Passed GroupSelectedValueUpdatesWhenSelectedButtonValueUpdates [< 1 ms]
Passed GroupNullSelectionClearsAnySelection [< 1 ms]
Passed ValuePropertyCanBeSetToNull [< 1 ms]
Passed ImpliedGroupDoesNotIncludeExplicitGroups [< 1 ms]
Passed RadioButtonGroupWorksWithContentViewControlTemplate [5 ms]
Passed RadioButtonAddedToGroupGetsGroupName [< 1 ms]
Passed RadioButtonGroupWorksWithDynamicallyAddedDescendants [1 ms]
Passed RadioButtonGroupSelectedValueBindingWorksWithNestedDescendants [< 1 ms]
Passed ImpliedGroup [< 1 ms]
Passed ThereCanBeOnlyOne [< 1 ms]
Passed NestedRadioButtonAddedToGroupGetsGroupName [< 1 ms]
Passed UpdatedGroupNameAppliesToRadioButtonsWithOldGroupName [< 1 ms]
Passed RadioButtonAddedToGroupKeepsGroupName [< 1 ms]
Passed RemovingSelectedButtonFromGroupClearsSelection [< 1 ms]
[xUnit.net 00:00:01.61] Finished: Microsoft.Maui.Controls.Core.UnitTests
Passed RadioButtonGroupAutoChecksMatchingButtonInContentViewWhenSelectedValuePreset [< 1 ms]
Test Run Successful.
Total tests: 19
Passed: 19
Total time: 2.1254 Seconds
📁 Fix files reverted (3 files)
eng/pipelines/ci-copilot.ymlsrc/Controls/src/Core/RadioButton/RadioButtonGroup.cssrc/Controls/src/Core/RadioButton/RadioButtonGroupController.cs
🤖 AI Summary
📊 Review Session —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34781 | (1) Guard AddRadioButton equality check with SelectedValue is not null; (2) fallback to RadioButtonGroupController.GetGroupController(radioButton)?.Layout in UncheckOtherRadioButtonsInScope |
✅ PASSED (Gate) | RadioButtonGroup.cs, RadioButtonGroupController.cs, RadioButtonTests.cs |
Original PR |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Fix GetVisualRoot to return topmost ancestor instead of null; guard AddRadioButton with radioButton.Value is not null (mirrors SetSelectedValue pattern) |
✅ PASS | 2 files | No new API surface |
| 2 | try-fix (claude-sonnet-4.6) | Walk up ancestor tree to find GroupName container as first fallback; guard both values non-null in AddRadioButton | ✅ PASS | 2 files | Semantic approach - no controller changes |
| 3 | try-fix (gpt-5.3-codex) | Controller-driven uncheck via _layout.Descendants(); guard AddRadioButton with radioButton.IsSet(ValueProperty) |
✅ PASS | 2 files | Uses IsSet check instead of null guard |
| 4 | try-fix (gpt-5.4) | Controller-owned selection sync; explicit SelectedValue reapply replaces direct equality check in AddRadioButton | ✅ PASS | 2 files | Most controller-centric approach |
| PR | PR #34781 | Guard AddRadioButton with SelectedValue is not null; fallback root to GetGroupController().Layout then radioButton.Parent |
✅ PASSED (Gate) | 3 files | Adds internal Element Layout property |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-sonnet-4.6 | 2 | No | NO NEW IDEAS |
Exhausted: Yes
Selected Fix: PR #34781 — Focused minimal fix addressing both bugs with clear comments. All alternatives also pass, but PR's fix adds targeted fallback logic at the right level.
📋 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | 2 bugs identified, 3 files changed |
| Gate | ✅ PASSED | android — tests fail without fix, pass with fix |
| Try-Fix | ✅ COMPLETE | 4 attempts, all 4 passing |
| Report | ✅ COMPLETE |
Summary
PR #34781 fixes a .NET 10 regression in RadioButtonGroup that broke grouping when RadioButton controls are inside ContentView/ControlTemplate. The fix correctly addresses two distinct bugs introduced by PR #32640, adds two well-targeted unit tests, and is confirmed correct by Gate and all 4 independent try-fix attempts.
Root Cause
PR #32640 removed the coerceValue and Value = this initializer from RadioButton, making RadioButton.Value default to null. This introduced two bugs:
- Auto-check bug:
object.Equals(null, null) == trueinAddRadioButtoncaused every button added via ControlTemplate to be immediately auto-checked, leaving all buttons in a checked state simultaneously. - Root resolution bug:
GetVisualRoot()returns null when the layout is not yet attached to a Page (which happens during ControlTemplate inflation). The oldradioButton.Parentfallback pointed to the immediateBordercontainer (single button), not the group layout — so sibling buttons were never found to uncheck.
Fix Quality
The fix is minimal, focused, and correct:
- Bug 1: Guards
AddRadioButtonwiththis.SelectedValue is not nullbefore the equality check. This is the right guard —SelectedValuestarts as null by default, meaning no pre-selection; only compare when it's been explicitly set. - Bug 2: Falls back to
RadioButtonGroupController.GetGroupController(radioButton)?.Layoutwhich reliably references the group's container layout (already registered viaAddRadioButton→UpdateGroupName). The final(Element)radioButton.Parentfallback preserves any remaining edge-case compatibility. - The
internal Element Layout => _layout;property exposes a read-only reference that was always available; it adds no behavioral change to the controller, only access. - Comments are thorough and clearly explain why each guard is needed.
Try-Fix comparison:
- 4 alternative approaches all passed — confirms the bug is definitively fixable.
- Attempt 1 (modify
GetVisualRoot): Works but changesGetVisualRoot's contract (now returns non-null even without a Page ancestor), which could affect other callers. - Attempt 2 (GroupName ancestor walk): Semantically clean but adds traversal overhead on every uncheck.
- Attempt 3 (
IsSet(ValueProperty)guard): Also correct butIsSetis a slightly stronger contract change. - Attempt 4 (controller-owned sync): Most invasive refactor.
- PR's fix is the narrowest, safest change of all candidates.
Tests: Two new unit tests cover both the primary regression (null/null auto-check through ContentView ControlTemplate) and the positive auto-check path (pre-set SelectedValue correctly auto-checks matching button). Both tests use AddLogicalChild to accurately simulate ControlTemplate inflation ordering, which is the real-world trigger.
Selected Fix: PR #34781 (original PR fix)
<!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Detail RadioButtonGroup is not functioning correctly when RadioButton controls are placed inside a ContentView in .NET 10. ### Root Cause PR [#32640](#32604) (fixing #32466) removed the coerceValue and Value = this initialiser from RadioButton, so RadioButton.Value now defaults to null. This introduced two distinct bugs in the ContentView+ControlTemplate scenario: - AddRadioButton guard object.Equals(radioButton.Value, this.SelectedValue) evaluates to object.Equals(null, null) == true, causing every button added via a ControlTemplate to be auto-checked immediately. - When the layout is not yet attached to a Page (common during ControlTemplate inflation), GetVisualRoot() returns null. The original fallback radioButton.Parent resolves to the button's immediate parent (Border), which only contains that single button — so other RadioButtons in the same group are never found and never unchecked. ### Description of Change - Updated the equality check in AddRadioButton to ensure comparison only happens when SelectedValue is not null. This avoids unintended selection during initialization. - Improved the root resolution logic in UncheckOtherRadioButtonsInScope. If no visual root is found, fallback to the RadioButtonGroupController’s layout (group container). ### Issue Fixed Fixes #34759 ### Screenshots | Before | After | |---------|--------| | <video src="https://github.com/user-attachments/assets/39e1e45d-dd04-42b7-bb85-aea83124b0cb"> | <video src="https://github.com/user-attachments/assets/252587b2-10ae-4eb9-968e-de114756aa0c"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Detail RadioButtonGroup is not functioning correctly when RadioButton controls are placed inside a ContentView in .NET 10. ### Root Cause PR [dotnet#32640](dotnet#32604) (fixing dotnet#32466) removed the coerceValue and Value = this initialiser from RadioButton, so RadioButton.Value now defaults to null. This introduced two distinct bugs in the ContentView+ControlTemplate scenario: - AddRadioButton guard object.Equals(radioButton.Value, this.SelectedValue) evaluates to object.Equals(null, null) == true, causing every button added via a ControlTemplate to be auto-checked immediately. - When the layout is not yet attached to a Page (common during ControlTemplate inflation), GetVisualRoot() returns null. The original fallback radioButton.Parent resolves to the button's immediate parent (Border), which only contains that single button — so other RadioButtons in the same group are never found and never unchecked. ### Description of Change - Updated the equality check in AddRadioButton to ensure comparison only happens when SelectedValue is not null. This avoids unintended selection during initialization. - Improved the root resolution logic in UncheckOtherRadioButtonsInScope. If no visual root is found, fallback to the RadioButtonGroupController’s layout (group container). ### Issue Fixed Fixes dotnet#34759 ### Screenshots | Before | After | |---------|--------| | <video src="https://github.com/user-attachments/assets/39e1e45d-dd04-42b7-bb85-aea83124b0cb"> | <video src="https://github.com/user-attachments/assets/252587b2-10ae-4eb9-968e-de114756aa0c"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Detail RadioButtonGroup is not functioning correctly when RadioButton controls are placed inside a ContentView in .NET 10. ### Root Cause PR [#32640](#32604) (fixing #32466) removed the coerceValue and Value = this initialiser from RadioButton, so RadioButton.Value now defaults to null. This introduced two distinct bugs in the ContentView+ControlTemplate scenario: - AddRadioButton guard object.Equals(radioButton.Value, this.SelectedValue) evaluates to object.Equals(null, null) == true, causing every button added via a ControlTemplate to be auto-checked immediately. - When the layout is not yet attached to a Page (common during ControlTemplate inflation), GetVisualRoot() returns null. The original fallback radioButton.Parent resolves to the button's immediate parent (Border), which only contains that single button — so other RadioButtons in the same group are never found and never unchecked. ### Description of Change - Updated the equality check in AddRadioButton to ensure comparison only happens when SelectedValue is not null. This avoids unintended selection during initialization. - Improved the root resolution logic in UncheckOtherRadioButtonsInScope. If no visual root is found, fallback to the RadioButtonGroupController’s layout (group container). ### Issue Fixed Fixes #34759 ### Screenshots | Before | After | |---------|--------| | <video src="https://github.com/user-attachments/assets/39e1e45d-dd04-42b7-bb85-aea83124b0cb"> | <video src="https://github.com/user-attachments/assets/252587b2-10ae-4eb9-968e-de114756aa0c"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Detail RadioButtonGroup is not functioning correctly when RadioButton controls are placed inside a ContentView in .NET 10. ### Root Cause PR [#32640](#32604) (fixing #32466) removed the coerceValue and Value = this initialiser from RadioButton, so RadioButton.Value now defaults to null. This introduced two distinct bugs in the ContentView+ControlTemplate scenario: - AddRadioButton guard object.Equals(radioButton.Value, this.SelectedValue) evaluates to object.Equals(null, null) == true, causing every button added via a ControlTemplate to be auto-checked immediately. - When the layout is not yet attached to a Page (common during ControlTemplate inflation), GetVisualRoot() returns null. The original fallback radioButton.Parent resolves to the button's immediate parent (Border), which only contains that single button — so other RadioButtons in the same group are never found and never unchecked. ### Description of Change - Updated the equality check in AddRadioButton to ensure comparison only happens when SelectedValue is not null. This avoids unintended selection during initialization. - Improved the root resolution logic in UncheckOtherRadioButtonsInScope. If no visual root is found, fallback to the RadioButtonGroupController’s layout (group container). ### Issue Fixed Fixes #34759 ### Screenshots | Before | After | |---------|--------| | <video src="https://github.com/user-attachments/assets/39e1e45d-dd04-42b7-bb85-aea83124b0cb"> | <video src="https://github.com/user-attachments/assets/252587b2-10ae-4eb9-968e-de114756aa0c"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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! <!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> ### Issue Detail RadioButtonGroup is not functioning correctly when RadioButton controls are placed inside a ContentView in .NET 10. ### Root Cause PR [#32640](#32604) (fixing #32466) removed the coerceValue and Value = this initialiser from RadioButton, so RadioButton.Value now defaults to null. This introduced two distinct bugs in the ContentView+ControlTemplate scenario: - AddRadioButton guard object.Equals(radioButton.Value, this.SelectedValue) evaluates to object.Equals(null, null) == true, causing every button added via a ControlTemplate to be auto-checked immediately. - When the layout is not yet attached to a Page (common during ControlTemplate inflation), GetVisualRoot() returns null. The original fallback radioButton.Parent resolves to the button's immediate parent (Border), which only contains that single button — so other RadioButtons in the same group are never found and never unchecked. ### Description of Change - Updated the equality check in AddRadioButton to ensure comparison only happens when SelectedValue is not null. This avoids unintended selection during initialization. - Improved the root resolution logic in UncheckOtherRadioButtonsInScope. If no visual root is found, fallback to the RadioButtonGroupController’s layout (group container). ### Issue Fixed Fixes #34759 ### Screenshots | Before | After | |---------|--------| | <video src="https://github.com/user-attachments/assets/39e1e45d-dd04-42b7-bb85-aea83124b0cb"> | <video src="https://github.com/user-attachments/assets/252587b2-10ae-4eb9-968e-de114756aa0c"> | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 Detail
RadioButtonGroup is not functioning correctly when RadioButton controls are placed inside a ContentView in .NET 10.
Root Cause
PR #32640 (fixing #32466) removed the coerceValue and Value = this initialiser from RadioButton, so RadioButton.Value now defaults to null. This introduced two distinct bugs in the ContentView+ControlTemplate scenario:
Description of Change
Issue Fixed
Fixes #34759
Screenshots
34759_BeforeFix.mov
34759_AfterFix.mov