From 9fd5f9854296c6e473b9a232cb2146607195f33e Mon Sep 17 00:00:00 2001 From: "Simon Zhao (BEYONDSOFT CONSULTING INC)" Date: Fri, 7 Aug 2026 10:35:33 +0800 Subject: [PATCH 1/2] Fix issue 14859: CheckBox and RadioButton ignore explicit BackColor values in VisualStylesMode.Net11 --- .../ButtonInternal/CheckBoxModernAdapter.cs | 18 +++++++++---- .../RadioButtonModernAdapter.cs | 18 +++++++++---- .../System/Windows/Forms/CheckBoxTests.cs | 26 +++++++++++++++++++ .../System/Windows/Forms/RadioButtonTests.cs | 26 +++++++++++++++++++ 4 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs index 11e6d11d5fd..0515c4178db 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs @@ -80,11 +80,19 @@ internal override LayoutOptions CommonLayout() private void PaintCore(PaintEventArgs e) { Graphics graphics = e.GraphicsInternal; - ParentBackgroundRenderer.Paint( - Control, - graphics, - Control.ClientRectangle, - Control.BackColor); + if (!Control.UseVisualStyleBackColor && !Control.BackColor.HasTransparency()) + { + using var backBrush = Control.BackColor.GetCachedSolidBrushScope(); + graphics.FillRectangle(backBrush, Control.ClientRectangle); + } + else + { + ParentBackgroundRenderer.Paint( + Control, + graphics, + Control.ClientRectangle, + Control.BackColor); + } LayoutData layout = Layout(e).Layout(); AdjustFocusRectangle(layout); diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs index bbd78441fb1..973e3e8afdb 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs @@ -79,11 +79,19 @@ internal override LayoutOptions CommonLayout() private void PaintCore(PaintEventArgs e) { Graphics graphics = e.GraphicsInternal; - ParentBackgroundRenderer.Paint( - Control, - graphics, - Control.ClientRectangle, - Control.BackColor); + if (!Control.UseVisualStyleBackColor && !Control.BackColor.HasTransparency()) + { + using var backBrush = Control.BackColor.GetCachedSolidBrushScope(); + graphics.FillRectangle(backBrush, Control.ClientRectangle); + } + else + { + ParentBackgroundRenderer.Paint( + Control, + graphics, + Control.ClientRectangle, + Control.BackColor); + } LayoutData layout = Layout(e).Layout(); AdjustFocusRectangle(layout); diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs index 7a9033f3f64..861fb6e4fa1 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs @@ -582,6 +582,7 @@ public void CheckBox_ModernGlyph_RendersAccentForCheckedStates(CheckState checkS using CheckBox box = new() { BackColor = Color.Red, + UseVisualStyleBackColor = true, CheckState = checkState, Size = new Size(40, 24), VisualStylesMode = VisualStylesMode.Net11 @@ -597,6 +598,31 @@ public void CheckBox_ModernGlyph_RendersAccentForCheckedStates(CheckState checkS Assert.Equal(expectedAccent, CountPixels(bitmap, Color.Red) > 0); } + [WinFormsFact] + public void CheckBox_ModernGlyph_UsesExplicitBackColorWhenVisualStyleBackgroundDisabled() + { + using Panel parent = new() { BackColor = Color.White }; + using CheckBox box = new() + { + BackColor = Color.Aqua, + CheckState = CheckState.Unchecked, + Text = string.Empty, + Size = new Size(40, 24), + VisualStylesMode = VisualStylesMode.Net11 + }; + + parent.Controls.Add(box); + + using Bitmap bitmap = new(box.Width, box.Height); + using Graphics graphics = Graphics.FromImage(bitmap); + PaintEventArgs e = new(graphics, box.ClientRectangle); + + box.CreateStandardAdapter().PaintUp(e, box.CheckState); + + Color backgroundPixel = bitmap.GetPixel(box.Width - 2, box.Height / 2); + Assert.Equal(Color.Aqua.ToArgb(), backgroundPixel.ToArgb()); + } + [WinFormsFact] public void CheckBox_ModernGlyph_DefaultCheckedColorUsesWindowsAccent() { diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs index 5ea64b41180..3747c1134a2 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs @@ -220,6 +220,7 @@ public void RadioButton_ModernGlyph_RendersAccentWhenChecked(bool isChecked, boo using RadioButton control = new() { BackColor = Color.Red, + UseVisualStyleBackColor = true, Checked = isChecked, Size = new Size(40, 24), VisualStylesMode = VisualStylesMode.Net11 @@ -237,6 +238,31 @@ public void RadioButton_ModernGlyph_RendersAccentWhenChecked(bool isChecked, boo Assert.Equal(expectedAccent, CountPixels(bitmap, Color.Red) > 0); } + [WinFormsFact] + public void RadioButton_ModernGlyph_UsesExplicitBackColorWhenVisualStyleBackgroundDisabled() + { + using Panel parent = new() { BackColor = Color.White }; + using RadioButton control = new() + { + BackColor = Color.Aqua, + Checked = false, + Text = string.Empty, + Size = new Size(40, 24), + VisualStylesMode = VisualStylesMode.Net11 + }; + + parent.Controls.Add(control); + + using Bitmap bitmap = new(control.Width, control.Height); + using Graphics graphics = Graphics.FromImage(bitmap); + PaintEventArgs e = new(graphics, control.ClientRectangle); + + control.CreateStandardAdapter().PaintUp(e, CheckState.Unchecked); + + Color backgroundPixel = bitmap.GetPixel(control.Width - 2, control.Height / 2); + Assert.Equal(Color.Aqua.ToArgb(), backgroundPixel.ToArgb()); + } + [WinFormsFact] public void RadioButton_ModernGlyph_DefaultCheckedColorUsesWindowsAccent() { From d95d37fd601ac18bf32512251a011f584f8467ed Mon Sep 17 00:00:00 2001 From: "Simon Zhao (BEYONDSOFT CONSULTING INC)" Date: Mon, 10 Aug 2026 14:19:51 +0800 Subject: [PATCH 2/2] Sync the latest code and apply relevant fixes. --- .../ButtonInternal/CheckBoxModernAdapter.cs | 21 +++++++++++-------- .../RadioButtonModernAdapter.cs | 21 +++++++++++-------- .../System/Windows/Forms/CheckBoxTests.cs | 8 +++++-- .../System/Windows/Forms/RadioButtonTests.cs | 8 +++++-- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs index b56ca9c1de8..5634dab7b7b 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/CheckBoxModernAdapter.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; @@ -80,7 +80,10 @@ internal override LayoutOptions CommonLayout() private void PaintCore(PaintEventArgs e) { Graphics graphics = e.GraphicsInternal; - if (!Control.UseVisualStyleBackColor && !Control.BackColor.HasTransparency()) + bool useControlBackColor = !Control.BackColor.HasTransparency() + && (Control.ShouldSerializeBackColor() || !Control.UseVisualStyleBackColor); + + if (useControlBackColor) { using var backBrush = Control.BackColor.GetCachedSolidBrushScope(); graphics.FillRectangle(backBrush, Control.ClientRectangle); @@ -98,11 +101,6 @@ private void PaintCore(PaintEventArgs e) AdjustFocusRectangle(layout); PaintBackgroundImage(e); - Color? customOnColor = Control.ShouldSerializeBackColor() - && Control.BackColor.A == byte.MaxValue - ? Control.BackColor - : null; - Color? customBorderColor = Control.FlatAppearance.BorderColor.IsEmpty ? null : Control.FlatAppearance.BorderColor; @@ -115,7 +113,7 @@ private void PaintCore(PaintEventArgs e) Control.Enabled, Control.MouseIsOver, Control.Focused && Control.ShowFocusCues, - customOnColor, + customOnColor: null, customBorderColor); PaintImage(e, layout); @@ -125,11 +123,16 @@ private void PaintCore(PaintEventArgs e) : Application.IsDarkModeEnabled ? Color.FromArgb(0xF0, 0xF0, 0xF0) : SystemColors.WindowText; + Color disabledTextBackColor = Control.ShouldSerializeBackColor() + && Control.BackColor.A == byte.MaxValue + ? Control.BackColor + : Control.Parent?.BackColor ?? Control.BackColor; + Color textColor = Control.Enabled ? preferredTextColor : ModernControlColorMath.GetDisabledTextColor( preferredTextColor, - Control.Parent?.BackColor ?? Control.BackColor); + disabledTextBackColor); PaintField(e, layout, PaintRender(e).Calculate(), textColor, drawFocus: true); } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs index 0fef517622b..86a49fc998a 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/RadioButtonModernAdapter.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Drawing; @@ -79,7 +79,10 @@ internal override LayoutOptions CommonLayout() private void PaintCore(PaintEventArgs e) { Graphics graphics = e.GraphicsInternal; - if (!Control.UseVisualStyleBackColor && !Control.BackColor.HasTransparency()) + bool useControlBackColor = !Control.BackColor.HasTransparency() + && (Control.ShouldSerializeBackColor() || !Control.UseVisualStyleBackColor); + + if (useControlBackColor) { using var backBrush = Control.BackColor.GetCachedSolidBrushScope(); graphics.FillRectangle(backBrush, Control.ClientRectangle); @@ -97,11 +100,6 @@ private void PaintCore(PaintEventArgs e) AdjustFocusRectangle(layout); PaintBackgroundImage(e); - Color? customOnColor = Control.ShouldSerializeBackColor() - && Control.BackColor.A == byte.MaxValue - ? Control.BackColor - : null; - Color? customBorderColor = Control.FlatAppearance.BorderColor.IsEmpty ? null : Control.FlatAppearance.BorderColor; @@ -114,7 +112,7 @@ private void PaintCore(PaintEventArgs e) Control.Enabled, Control.MouseIsOver, Control.Focused && Control.ShowFocusCues, - customOnColor, + customOnColor: null, customBorderColor); PaintImage(e, layout); @@ -124,11 +122,16 @@ private void PaintCore(PaintEventArgs e) : Application.IsDarkModeEnabled ? Color.FromArgb(0xF0, 0xF0, 0xF0) : SystemColors.WindowText; + Color disabledTextBackColor = Control.ShouldSerializeBackColor() + && Control.BackColor.A == byte.MaxValue + ? Control.BackColor + : Control.Parent?.BackColor ?? Control.BackColor; + Color textColor = Control.Enabled ? preferredTextColor : ModernControlColorMath.GetDisabledTextColor( preferredTextColor, - Control.Parent?.BackColor ?? Control.BackColor); + disabledTextBackColor); PaintField(e, layout, PaintRender(e).Calculate(), textColor, drawFocus: true); } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs index 4c730c9940f..b96ab2bd694 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/CheckBoxTests.cs @@ -571,7 +571,7 @@ public void CheckBox_AppearanceChanged_RecreatesModernAdapter() [InlineData(CheckState.Unchecked, false)] [InlineData(CheckState.Checked, true)] [InlineData(CheckState.Indeterminate, true)] - public void CheckBox_ModernGlyph_RendersAccentForCheckedStates(CheckState checkState, bool expectedAccent) + public void CheckBox_ModernGlyph_UsesExplicitBackColorWithoutTintingCheckedGlyph(CheckState checkState, bool expectedAccent) { if (SystemInformation.HighContrast) { @@ -595,7 +595,11 @@ public void CheckBox_ModernGlyph_RendersAccentForCheckedStates(CheckState checkS box.CreateStandardAdapter().PaintUp(e, checkState); - Assert.Equal(expectedAccent, CountPixels(bitmap, Color.Red) > 0); + Color backgroundPixel = bitmap.GetPixel(box.Width - 2, box.Height / 2); + Assert.Equal(Color.Red.ToArgb(), backgroundPixel.ToArgb()); + Assert.Equal( + expectedAccent, + CountPixels(bitmap, Application.SystemVisualSettings.AccentColor) > 0); } [WinFormsFact] diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs index 08fdf267d4a..59aaf621539 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/RadioButtonTests.cs @@ -209,7 +209,7 @@ public void RadioButton_AppearanceChanged_RecreatesModernAdapter() [WinFormsTheory] [InlineData(false, false)] [InlineData(true, true)] - public void RadioButton_ModernGlyph_RendersAccentWhenChecked(bool isChecked, bool expectedAccent) + public void RadioButton_ModernGlyph_UsesExplicitBackColorWithoutTintingCheckedGlyph(bool isChecked, bool expectedAccent) { if (SystemInformation.HighContrast) { @@ -235,7 +235,11 @@ public void RadioButton_ModernGlyph_RendersAccentWhenChecked(bool isChecked, boo e, isChecked ? CheckState.Checked : CheckState.Unchecked); - Assert.Equal(expectedAccent, CountPixels(bitmap, Color.Red) > 0); + Color backgroundPixel = bitmap.GetPixel(control.Width - 2, control.Height / 2); + Assert.Equal(Color.Red.ToArgb(), backgroundPixel.ToArgb()); + Assert.Equal( + expectedAccent, + CountPixels(bitmap, Application.SystemVisualSettings.AccentColor, channelTolerance: 24) > 0); } [WinFormsFact]