Skip to content

Commit 5693427

Browse files
committed
fix #13813 : Dark Mode: The ForeColor & BackColor properties are not working for the controls
1 parent 8818780 commit 5693427

File tree

9 files changed

+90
-22
lines changed

9 files changed

+90
-22
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/Button.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ private protected override bool OwnerDraw
170170
// ...the user wants to opt out of implicit DarkMode rendering.
171171
&& DarkModeRequestState is true
172172

173+
&& !ForeColorSet
174+
&& !BackColorSet
173175
// And all of this only counts for FlatStyle.Standard. For the
174176
// rest, we're using specific renderers anyway, which check
175177
// themselves on demand, if they need to apply Light- or DarkMode.

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: 54 additions & 5 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+
(_, textColor1, drawFocus) => 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),
86134
(_, textColor, drawFocus) => 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),
123172
(_, textColor, drawFocus) => 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: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public void RenderButton(
3737
bool focused,
3838
bool showFocusCues,
3939
Color parentBackgroundColor,
40+
Color backColor,
4041
Action<Rectangle> paintImage,
4142
Action<Rectangle, Color, bool> paintField)
4243
{
@@ -60,7 +61,7 @@ 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);
@@ -78,9 +79,11 @@ public void RenderButton(
7879
}
7980
}
8081

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

8384
public abstract void DrawFocusIndicator(Graphics graphics, Rectangle contentBounds, bool isDefault);
8485

8586
public abstract Color GetTextColor(PushButtonState state, bool isDefault);
87+
88+
public abstract Color GetBackgroundColor(PushButtonState state, bool isDefault);
8689
}

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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ void RenderButton(
4848
bool focused,
4949
bool showFocusCues,
5050
Color parentBackgroundColor,
51+
Color backColor,
5152
Action<Rectangle> paintImage,
5253
Action<Rectangle, Color, bool> paintField);
5354

@@ -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)