Skip to content

Commit 6bce8b5

Browse files
fix #13813 : Dark Mode: The ForeColor & BackColor properties are not working for the controls (#13818)
Fixes #13813 ## Proposed changes Add code to see if BackColor or ForeColor is specified in dark mode, if so then use the specified color, if not then let the renderer decide the color. <!-- ## Customer Impact - - ## Regression? - Yes / No ## Risk - --> ## Screenshots ### Before <img width="589" height="574" alt="image" src="https://github.com/user-attachments/assets/b5c2281e-937f-4e44-8841-552d22fceed7" /> ### After <img width="590" height="571" alt="image" src="https://github.com/user-attachments/assets/9604e7ce-1d69-4b63-a0e1-b57938f9752d" /> <!-- ## Test methodology - - - ## Accessibility testing ## Test environment(s) -->
2 parents 3774d9f + fa70402 commit 6bce8b5

File tree

8 files changed

+93
-30
lines changed

8 files changed

+93
-30
lines changed

src/System.Windows.Forms/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
override System.Windows.Forms.ButtonBase.OnBackColorChanged(System.EventArgs! e) -> void
2+
override System.Windows.Forms.ButtonBase.OnForeColorChanged(System.EventArgs! e) -> void
13
static System.Windows.Forms.Application.SetColorMode(System.Windows.Forms.SystemColorMode systemColorMode) -> void
24
static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task<System.Windows.Forms.TaskDialogButton!>!
35
static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task<System.Windows.Forms.TaskDialogButton!>!

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,23 @@ protected override void OnKeyDown(KeyEventArgs kevent)
12021202
base.OnKeyDown(kevent);
12031203
}
12041204

1205+
internal bool BackColorSet { get; set; }
1206+
internal bool ForeColorSet { get; set; }
1207+
1208+
protected override void OnForeColorChanged(EventArgs e)
1209+
{
1210+
base.OnForeColorChanged(e);
1211+
ForeColorSet = ShouldSerializeForeColor();
1212+
UpdateOwnerDraw();
1213+
}
1214+
1215+
protected override void OnBackColorChanged(EventArgs e)
1216+
{
1217+
base.OnBackColorChanged(e);
1218+
BackColorSet = ShouldSerializeBackColor();
1219+
UpdateOwnerDraw();
1220+
}
1221+
12051222
/// <summary>
12061223
/// Raises the <see cref="OnKeyUp"/> event.
12071224
/// </summary>

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeAdapter.cs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,51 @@ internal ButtonDarkModeAdapter(ButtonBase control) : base(control)
2525
private ButtonDarkModeRendererBase ButtonDarkModeRenderer =>
2626
_buttonDarkModeRenderer;
2727

28+
private Color GetButtonTextColor(IDeviceContext deviceContext, PushButtonState state)
29+
{
30+
Color textColor;
31+
32+
if (Control.ForeColorSet)
33+
{
34+
textColor = new ColorOptions(deviceContext, Control.ForeColor, Control.BackColor)
35+
{
36+
Enabled = Control.Enabled
37+
}.Calculate().WindowText;
38+
39+
if (IsHighContrastHighlighted())
40+
{
41+
textColor = SystemColors.HighlightText;
42+
}
43+
}
44+
else
45+
{
46+
textColor = ButtonDarkModeRenderer.GetTextColor(state, Control.IsDefault);
47+
}
48+
49+
return textColor;
50+
}
51+
52+
private Color GetButtonBackColor(PushButtonState state)
53+
{
54+
Color textColor;
55+
56+
if (Control.BackColorSet)
57+
{
58+
textColor = Control.BackColor;
59+
60+
if (IsHighContrastHighlighted())
61+
{
62+
textColor = SystemColors.HighlightText;
63+
}
64+
}
65+
else
66+
{
67+
textColor = ButtonDarkModeRenderer.GetBackgroundColor(state, Control.IsDefault);
68+
}
69+
70+
return textColor;
71+
}
72+
2873
internal override void PaintUp(PaintEventArgs e, CheckState state)
2974
{
3075
try
@@ -36,21 +81,23 @@ internal override void PaintUp(PaintEventArgs e, CheckState state)
3681

3782
LayoutData layout = CommonLayout().Layout();
3883

84+
PushButtonState pushButtonState = ToPushButtonState(state, Control.Enabled);
3985
ButtonDarkModeRenderer.RenderButton(
4086
g,
4187
Control.ClientRectangle,
4288
Control.FlatStyle,
43-
ToPushButtonState(state, Control.Enabled),
89+
pushButtonState,
4490
Control.IsDefault,
4591
Control.Focused,
4692
Control.ShowFocusCues,
4793
Control.Parent?.BackColor ?? Control.BackColor,
94+
GetButtonBackColor(pushButtonState),
4895
_ => PaintImage(e, layout),
49-
(_, textColor, drawFocus) => PaintField(
96+
() => PaintField(
5097
e,
5198
layout,
5299
PaintDarkModeRender(e).Calculate(),
53-
textColor,
100+
GetButtonTextColor(e, pushButtonState),
54101
drawFocus: false)
55102
);
56103

@@ -82,12 +129,13 @@ internal override void PaintDown(PaintEventArgs e, CheckState state)
82129
Control.Focused,
83130
Control.ShowFocusCues,
84131
Control.Parent?.BackColor ?? Control.BackColor,
132+
GetButtonBackColor(PushButtonState.Pressed),
85133
_ => PaintImage(e, layout),
86-
(_, textColor, drawFocus) => PaintField(
134+
() => PaintField(
87135
e,
88136
layout,
89137
PaintDarkModeRender(e).Calculate(),
90-
textColor,
138+
GetButtonTextColor(e, PushButtonState.Pressed),
91139
drawFocus: false)
92140
);
93141

@@ -119,12 +167,13 @@ internal override void PaintOver(PaintEventArgs e, CheckState state)
119167
Control.Focused,
120168
Control.ShowFocusCues,
121169
Control.Parent?.BackColor ?? Control.BackColor,
170+
GetButtonBackColor(PushButtonState.Hot),
122171
_ => PaintImage(e, layout),
123-
(_, textColor, drawFocus) => PaintField(
172+
() => PaintField(
124173
e,
125174
layout,
126175
PaintDarkModeRender(e).Calculate(),
127-
textColor,
176+
GetButtonTextColor(e, PushButtonState.Hot),
128177
drawFocus: false)
129178
);
130179

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/ButtonDarkModeRendererBase.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ public void RenderButton(
3737
bool focused,
3838
bool showFocusCues,
3939
Color parentBackgroundColor,
40+
Color backColor,
4041
Action<Rectangle> paintImage,
41-
Action<Rectangle, Color, bool> paintField)
42+
Action paintField)
4243
{
4344
ArgumentNullException.ThrowIfNull(graphics);
4445
ArgumentNullException.ThrowIfNull(paintImage);
@@ -60,15 +61,12 @@ public void RenderButton(
6061
height: bounds.Height - padding.Vertical);
6162

6263
// Draw button background and get content bounds
63-
Rectangle contentBounds = DrawButtonBackground(graphics, paddedBounds, state, isDefault);
64+
Rectangle contentBounds = DrawButtonBackground(graphics, paddedBounds, state, isDefault, backColor);
6465

6566
// Paint image and field using the provided delegates
6667
paintImage(contentBounds);
6768

68-
paintField(
69-
contentBounds,
70-
GetTextColor(state, isDefault),
71-
false);
69+
paintField();
7270

7371
if (focused && showFocusCues)
7472
{
@@ -78,9 +76,11 @@ public void RenderButton(
7876
}
7977
}
8078

81-
public abstract Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault);
79+
public abstract Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor);
8280

8381
public abstract void DrawFocusIndicator(Graphics graphics, Rectangle contentBounds, bool isDefault);
8482

8583
public abstract Color GetTextColor(PushButtonState state, bool isDefault);
84+
85+
public abstract Color GetBackgroundColor(PushButtonState state, bool isDefault);
8686
}

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/FlatButtonDarkModeRenderer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ internal sealed class FlatButtonDarkModeRenderer : ButtonDarkModeRendererBase
2323
private protected override Padding PaddingCore { get; } = new(0);
2424

2525
public override Rectangle DrawButtonBackground(
26-
Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault)
26+
Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor)
2727
{
2828
// fill background
29-
using var back = GetBackgroundColor(state, isDefault).GetCachedSolidBrushScope();
29+
using var back = backColor.GetCachedSolidBrushScope();
3030
graphics.FillRectangle(back, bounds);
3131

3232
// draw border identical to Win32
@@ -58,7 +58,7 @@ public override Color GetTextColor(PushButtonState state, bool isDefault) =>
5858
? DefaultColors.AcceptButtonTextColor
5959
: DefaultColors.NormalTextColor;
6060

61-
private static Color GetBackgroundColor(PushButtonState state, bool isDefault) =>
61+
public override Color GetBackgroundColor(PushButtonState state, bool isDefault) =>
6262
isDefault
6363
? state switch
6464
{

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/IButtonRenderer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ void RenderButton(
4848
bool focused,
4949
bool showFocusCues,
5050
Color parentBackgroundColor,
51+
Color backColor,
5152
Action<Rectangle> paintImage,
52-
Action<Rectangle, Color, bool> paintField);
53+
Action paintField);
5354

5455
/// <summary>
5556
/// Draws button background with appropriate styling.
@@ -59,7 +60,7 @@ void RenderButton(
5960
/// <param name="state">State of the button (normal, hot, pressed, disabled)</param>
6061
/// <param name="isDefault">True if button is the default button</param>
6162
/// <returns>The content bounds (area inside the button for text/image)</returns>
62-
Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault);
63+
Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor);
6364

6465
/// <summary>
6566
/// Draws focus indicator appropriate for this style.

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/PopupButtonDarkModeRenderer.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal class PopupButtonDarkModeRenderer : ButtonDarkModeRendererBase
3030
/// <summary>
3131
/// Draws button background with popup styling, including subtle 3D effect.
3232
/// </summary>
33-
public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault)
33+
public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor)
3434
{
3535
// Use padding from ButtonDarkModeRenderer
3636
Padding padding = PaddingCore;
@@ -45,9 +45,6 @@ public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle boun
4545
contentBounds.Offset(ContentOffset, ContentOffset);
4646
}
4747

48-
// Get appropriate background color based on state
49-
Color backColor = GetBackgroundColor(state, isDefault);
50-
5148
// Create path for rounded corners
5249
using GraphicsPath path = CreateRoundedRectanglePath(paddedBounds, ButtonCornerRadius);
5350

@@ -101,7 +98,7 @@ public override Color GetTextColor(PushButtonState state, bool isDefault) =>
10198
/// <summary>
10299
/// Gets the background color appropriate for the button state and type.
103100
/// </summary>
104-
private static Color GetBackgroundColor(PushButtonState state, bool isDefault) =>
101+
public override Color GetBackgroundColor(PushButtonState state, bool isDefault) =>
105102
isDefault
106103
? state switch
107104
{

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/DarkMode/SystemButtonDarkModeRenderer.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,13 @@ internal class SystemButtonDarkModeRenderer : ButtonDarkModeRendererBase
3030
/// <summary>
3131
/// Draws button background with system styling (larger rounded corners).
3232
/// </summary>
33-
public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault)
33+
public override Rectangle DrawButtonBackground(Graphics graphics, Rectangle bounds, PushButtonState state, bool isDefault, Color backColor)
3434
{
3535
// Shrink for DarkBorderGap and FocusBorderThickness
3636
Rectangle fillBounds = Rectangle.Inflate(bounds, -SystemStylePadding, -SystemStylePadding);
3737

3838
using GraphicsPath fillPath = CreateRoundedRectanglePath(fillBounds, CornerRadius - DarkBorderGapThickness);
3939

40-
// Get appropriate background color based on state
41-
Color backColor = GetBackgroundColor(state, isDefault);
42-
4340
// Fill the background using cached brush
4441
using var brush = backColor.GetCachedSolidBrushScope();
4542
graphics.FillPath(brush, fillPath);
@@ -82,7 +79,7 @@ public override Color GetTextColor(PushButtonState state, bool isDefault) =>
8279
/// <summary>
8380
/// Gets the background color appropriate for the button state and type.
8481
/// </summary>
85-
private static Color GetBackgroundColor(PushButtonState state, bool isDefault) =>
82+
public override Color GetBackgroundColor(PushButtonState state, bool isDefault) =>
8683
// For default button in System style, use a darker version of the background color
8784
isDefault
8885
? state switch
@@ -114,7 +111,7 @@ private static Color GetBackgroundColor(PushButtonState state, bool isDefault) =
114111
/// <summary>
115112
/// Draws the button border based on the current state, using anti-aliasing and an additional inner border.
116113
/// </summary>
117-
public static void DrawButtonBorder(
114+
public void DrawButtonBorder(
118115
Graphics graphics,
119116
Rectangle bounds,
120117
PushButtonState state,

0 commit comments

Comments
 (0)