Skip to content

Commit 21f0dbc

Browse files
DarkMode (a) Fix up System Control Button (UpDown, ComboBox, Ellipse-Button) (#13747)
Fixes rendering issues in dark mode rendering, where a button is used in the context of another control: * DropDownArrow * UpDown based controls * Ellipse-Control in Property browsers These buttons have not been rendered correctly in dark mode, and a new system control button renderer (tested by CTI) fixes these issues. Note that this PR does not change the Classic (Light Mode) code paths in any way. Examples: <img width="230" height="60" alt="image" src="https://github.com/user-attachments/assets/9bd4750b-076d-45cd-9160-997b50ff98ad" /> <img width="565" height="72" alt="image" src="https://github.com/user-attachments/assets/26c4eba5-11cd-44ad-8964-bb3d586fee41" /> <img width="763" height="151" alt="image" src="https://github.com/user-attachments/assets/ce4f409f-051b-422a-aa1f-26c5a8470e0f" /> ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/dotnet/winforms/pull/13747)
2 parents 255288f + bca4402 commit 21f0dbc

File tree

8 files changed

+763
-138
lines changed

8 files changed

+763
-138
lines changed

src/System.Windows.Forms/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/DropDownButton.cs

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Drawing;
55
using System.Windows.Forms.ButtonInternal;
66
using System.Windows.Forms.VisualStyles;
7+
using static System.Windows.Forms.ControlPaint;
78

89
namespace System.Windows.Forms.PropertyGridInternal;
910

@@ -17,7 +18,20 @@ public DropDownButton()
1718
SetAccessibleName();
1819
}
1920

20-
// When the holder is open, we don't fire clicks.
21+
/// <summary>
22+
/// Indicates whether the control should be rendered in dark mode.
23+
/// Set this property if you use this class for a control in dark mode.
24+
/// </summary>
25+
public bool RequestDarkModeRendering { get; set; }
26+
27+
/// <summary>
28+
/// Gets or sets the style used for rendering the control button.
29+
/// </summary>
30+
public ModernControlButtonStyle ControlButtonStyle { get; set; }
31+
32+
/// <summary>
33+
/// Gets or sets a value indicating whether mouse events should be ignored when the holder is open.
34+
/// </summary>
2135
public bool IgnoreMouse { get; set; }
2236

2337
/// <summary>
@@ -64,52 +78,82 @@ protected override void OnMouseDown(MouseEventArgs e)
6478
}
6579
}
6680

81+
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
6782
protected override void OnPaint(PaintEventArgs pevent)
6883
{
69-
base.OnPaint(pevent);
84+
ComboBoxState state = ComboBoxState.Normal;
7085

71-
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
72-
if (!Application.IsDarkModeEnabled
73-
&& (Application.RenderWithVisualStyles & _useComboBoxTheme))
86+
if (MouseIsDown)
7487
{
75-
ComboBoxState state = ComboBoxState.Normal;
76-
77-
if (MouseIsDown)
78-
{
79-
state = ComboBoxState.Pressed;
80-
}
81-
else if (MouseIsOver)
82-
{
83-
state = ComboBoxState.Hot;
84-
}
88+
state = ComboBoxState.Pressed;
89+
}
90+
else if (MouseIsOver)
91+
{
92+
state = ComboBoxState.Hot;
93+
}
8594

86-
Rectangle dropDownButtonRect = new(0, 0, Width, Height);
87-
if (state == ComboBoxState.Normal)
88-
{
89-
pevent.Graphics.FillRectangle(SystemBrushes.Window, dropDownButtonRect);
90-
}
95+
base.OnPaint(pevent);
9196

92-
using (DeviceContextHdcScope hdc = new(pevent))
97+
if (Application.IsDarkModeEnabled && RequestDarkModeRendering)
98+
{
99+
ModernControlButtonState buttonState = state switch
93100
{
94-
ComboBoxRenderer.DrawDropDownButtonForHandle(
95-
hdc,
96-
dropDownButtonRect,
97-
state,
98-
ScaleHelper.IsScalingRequirementMet ? HWNDInternal : HWND.Null);
99-
}
101+
ComboBoxState.Disabled => ModernControlButtonState.Disabled,
102+
ComboBoxState.Hot => ModernControlButtonState.Hover,
103+
ComboBoxState.Pressed => ModernControlButtonState.Pressed,
104+
_ => ModernControlButtonState.Normal
105+
};
106+
107+
DrawModernControlButton(
108+
pevent.Graphics,
109+
new Rectangle(0, 0, Width, Height),
110+
ControlButtonStyle,
111+
buttonState,
112+
isDarkMode: true);
113+
114+
return;
115+
}
100116

101-
// Redraw focus cues.
102-
//
103-
// For consistency with other PropertyGrid buttons, i.e. those opening system dialogs ("..."), that
104-
// always show visual cues when focused, we need to do the same for this custom button, painted as
105-
// a ComboBox control part (drop-down).
106-
if (Focused)
107-
{
108-
dropDownButtonRect.Inflate(-1, -1);
109-
ControlPaint.DrawFocusRectangle(pevent.Graphics, dropDownButtonRect, ForeColor, BackColor);
110-
}
117+
if (Application.RenderWithVisualStyles & _useComboBoxTheme)
118+
{
119+
RenderComboBoxButtonWithVisualStyles(pevent, state);
111120
}
121+
}
112122
#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
123+
private void RenderComboBoxButtonWithVisualStyles(PaintEventArgs pevent, ComboBoxState state)
124+
{
125+
Rectangle dropDownButtonRect = new(0, 0, Width, Height);
126+
127+
if (state == ComboBoxState.Normal)
128+
{
129+
pevent.Graphics.FillRectangle(
130+
SystemBrushes.Window,
131+
dropDownButtonRect);
132+
}
133+
134+
using (DeviceContextHdcScope hdc = new(pevent))
135+
{
136+
ComboBoxRenderer.DrawDropDownButtonForHandle(
137+
hdc,
138+
dropDownButtonRect,
139+
state,
140+
ScaleHelper.IsScalingRequirementMet ? HWNDInternal : HWND.Null);
141+
}
142+
143+
// Redraw focus cues.
144+
//
145+
// For consistency with other PropertyGrid buttons, i.e. those opening system dialogs ("..."), that
146+
// always show visual cues when focused, we need to do the same for this custom button, painted as
147+
// a ComboBox control part (drop-down).
148+
if (Focused)
149+
{
150+
dropDownButtonRect.Inflate(-1, -1);
151+
DrawFocusRectangle(
152+
pevent.Graphics,
153+
dropDownButtonRect,
154+
ForeColor,
155+
BackColor);
156+
}
113157
}
114158

115159
internal void PerformButtonClick()

src/System.Windows.Forms/System/Windows/Forms/Controls/PropertyGrid/PropertyGridInternal/PropertyGridView.cs

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.Win32;
1212
using Windows.Win32.System.Variant;
1313
using Windows.Win32.UI.Accessibility;
14+
using static System.Windows.Forms.ControlPaint;
1415

1516
namespace System.Windows.Forms.PropertyGridInternal;
1617

@@ -190,33 +191,44 @@ public bool CanUndo
190191
/// the selected row's <see cref="GridEntry"/>.
191192
/// </para>
192193
/// </remarks>
194+
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
193195
internal DropDownButton DropDownButton
194196
{
195197
get
196198
{
197-
if (_dropDownButton is null)
199+
if (_dropDownButton is not null)
198200
{
199-
OwnerGrid.CheckInCreate();
201+
return _dropDownButton;
202+
}
200203

201-
_dropDownButton = new()
202-
{
203-
UseComboBoxTheme = true
204-
};
204+
OwnerGrid.CheckInCreate();
205205

206-
Bitmap bitmap = CreateResizedBitmap("Arrow", DownArrowIconWidth, DownArrowIconHeight);
207-
_dropDownButton.Image = bitmap;
208-
_dropDownButton.BackColor = SystemColors.Control;
209-
_dropDownButton.ForeColor = SystemColors.ControlText;
210-
_dropDownButton.Click += OnButtonClick;
211-
_dropDownButton.GotFocus += OnDropDownButtonGotFocus;
212-
_dropDownButton.LostFocus += OnChildLostFocus;
213-
_dropDownButton.TabIndex = 2;
206+
_dropDownButton = new()
207+
{
208+
UseComboBoxTheme = true,
209+
RequestDarkModeRendering = Application.IsDarkModeEnabled,
210+
ControlButtonStyle = ModernControlButtonStyle.OpenDropDown | ModernControlButtonStyle.RoundedBorder
211+
};
214212

215-
CommonEditorSetup(_dropDownButton);
216-
_dropDownButton.Size = ScaleHelper.IsScalingRequirementMet
217-
? new(SystemInformation.VerticalScrollBarArrowHeightForDpi(DeviceDpiInternal), RowHeight)
218-
: new(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
219-
}
213+
Bitmap bitmap = CreateResizedBitmap(
214+
"Arrow",
215+
DownArrowIconWidth,
216+
DownArrowIconHeight);
217+
218+
// For classic mode/backwards compatibility.
219+
_dropDownButton.Image = bitmap;
220+
_dropDownButton.BackColor = SystemColors.Control;
221+
_dropDownButton.ForeColor = SystemColors.ControlText;
222+
_dropDownButton.Click += OnButtonClick;
223+
_dropDownButton.GotFocus += OnDropDownButtonGotFocus;
224+
_dropDownButton.LostFocus += OnChildLostFocus;
225+
_dropDownButton.TabIndex = 2;
226+
227+
CommonEditorSetup(_dropDownButton);
228+
229+
_dropDownButton.Size = ScaleHelper.IsScalingRequirementMet
230+
? new(SystemInformation.VerticalScrollBarArrowHeightForDpi(DeviceDpiInternal), RowHeight)
231+
: new(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
220232

221233
return _dropDownButton;
222234
}
@@ -235,32 +247,42 @@ internal Button DialogButton
235247
{
236248
get
237249
{
238-
if (_dialogButton is null)
250+
if (_dialogButton is not null)
239251
{
240-
OwnerGrid.CheckInCreate();
252+
return _dialogButton;
253+
}
241254

242-
_dialogButton = new DropDownButton
243-
{
244-
BackColor = SystemColors.Control,
245-
ForeColor = SystemColors.ControlText,
246-
TabIndex = 3,
247-
Image = CreateResizedBitmap("dotdotdot", DotDotDotIconWidth, DotDotDotIconHeight)
248-
};
255+
OwnerGrid.CheckInCreate();
249256

250-
_dialogButton.Click += OnButtonClick;
251-
_dialogButton.KeyDown += OnButtonKeyDown;
252-
_dialogButton.GotFocus += OnDropDownButtonGotFocus;
253-
_dialogButton.LostFocus += OnChildLostFocus;
254-
_dialogButton.Size = ScaleHelper.IsScalingRequirementMet
255-
? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(DeviceDpiInternal), RowHeight)
256-
: new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
257+
_dialogButton = new DropDownButton
258+
{
259+
RequestDarkModeRendering = Application.IsDarkModeEnabled,
260+
ControlButtonStyle = ModernControlButtonStyle.Ellipse | ModernControlButtonStyle.RoundedBorder,
261+
BackColor = SystemColors.Control,
262+
ForeColor = SystemColors.ControlText,
263+
TabIndex = 3,
257264

258-
CommonEditorSetup(_dialogButton);
259-
}
265+
// For classic mode/backwards compatibility.
266+
Image = CreateResizedBitmap(
267+
"dotdotdot",
268+
DotDotDotIconWidth,
269+
DotDotDotIconHeight)
270+
};
271+
272+
_dialogButton.Click += OnButtonClick;
273+
_dialogButton.KeyDown += OnButtonKeyDown;
274+
_dialogButton.GotFocus += OnDropDownButtonGotFocus;
275+
_dialogButton.LostFocus += OnChildLostFocus;
276+
_dialogButton.Size = ScaleHelper.IsScalingRequirementMet
277+
? new Size(SystemInformation.VerticalScrollBarArrowHeightForDpi(DeviceDpiInternal), RowHeight)
278+
: new Size(SystemInformation.VerticalScrollBarArrowHeight, RowHeight);
279+
280+
CommonEditorSetup(_dialogButton);
260281

261282
return _dialogButton;
262283
}
263284
}
285+
#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
264286

265287
/// <summary>
266288
/// The common text box for editing values.
@@ -269,30 +291,32 @@ private GridViewTextBox EditTextBox
269291
{
270292
get
271293
{
272-
if (_editTextBox is null)
294+
if (_editTextBox is not null)
273295
{
274-
OwnerGrid.CheckInCreate();
296+
return _editTextBox;
297+
}
275298

276-
_editTextBox = new(this)
277-
{
278-
BorderStyle = BorderStyle.None,
279-
AutoSize = false,
280-
TabStop = false,
281-
AcceptsReturn = true,
282-
BackColor = BackColor,
283-
ForeColor = ForeColor
284-
};
299+
OwnerGrid.CheckInCreate();
285300

286-
_editTextBox.KeyDown += OnEditKeyDown;
287-
_editTextBox.KeyPress += OnEditKeyPress;
288-
_editTextBox.GotFocus += OnEditGotFocus;
289-
_editTextBox.LostFocus += OnEditLostFocus;
290-
_editTextBox.MouseDown += OnEditMouseDown;
291-
_editTextBox.TextChanged += OnEditChange;
301+
_editTextBox = new(this)
302+
{
303+
BorderStyle = BorderStyle.None,
304+
AutoSize = false,
305+
TabStop = false,
306+
AcceptsReturn = true,
307+
BackColor = BackColor,
308+
ForeColor = ForeColor
309+
};
292310

293-
_editTextBox.TabIndex = 1;
294-
CommonEditorSetup(_editTextBox);
295-
}
311+
_editTextBox.KeyDown += OnEditKeyDown;
312+
_editTextBox.KeyPress += OnEditKeyPress;
313+
_editTextBox.GotFocus += OnEditGotFocus;
314+
_editTextBox.LostFocus += OnEditLostFocus;
315+
_editTextBox.MouseDown += OnEditMouseDown;
316+
_editTextBox.TextChanged += OnEditChange;
317+
318+
_editTextBox.TabIndex = 1;
319+
CommonEditorSetup(_editTextBox);
296320

297321
return _editTextBox;
298322
}
@@ -2210,7 +2234,7 @@ protected override void OnGotFocus(EventArgs e)
22102234
if ((Size.Width > doubleOffset) && (Size.Height > doubleOffset))
22112235
{
22122236
using Graphics g = CreateGraphicsInternal();
2213-
ControlPaint.DrawFocusRectangle(g, new Rectangle(_offset2Units, _offset2Units, Size.Width - doubleOffset, Size.Height - doubleOffset));
2237+
DrawFocusRectangle(g, new Rectangle(_offset2Units, _offset2Units, Size.Width - doubleOffset, Size.Height - doubleOffset));
22142238
}
22152239
}
22162240
}

0 commit comments

Comments
 (0)