Skip to content

Commit 4078032

Browse files
Improve DropDownButton Rendering in PropertyGrid.
1 parent 760890c commit 4078032

File tree

6 files changed

+362
-181
lines changed

6 files changed

+362
-181
lines changed

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

Lines changed: 73 additions & 36 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,6 +18,12 @@ public DropDownButton()
1718
SetAccessibleName();
1819
}
1920

21+
// If a control uses it needs it in the context of rendering
22+
// something in dark mode - this flag needs to be set.
23+
public bool RequestDarkModeRendering { get; set; }
24+
25+
public ModernControlButtonStyle ControlButtonStyle { get; set; }
26+
2027
// When the holder is open, we don't fire clicks.
2128
public bool IgnoreMouse { get; set; }
2229

@@ -64,52 +71,82 @@ protected override void OnMouseDown(MouseEventArgs e)
6471
}
6572
}
6673

74+
#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.
6775
protected override void OnPaint(PaintEventArgs pevent)
6876
{
69-
base.OnPaint(pevent);
77+
ComboBoxState state = ComboBoxState.Normal;
7078

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))
79+
if (MouseIsDown)
7480
{
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-
}
81+
state = ComboBoxState.Pressed;
82+
}
83+
else if (MouseIsOver)
84+
{
85+
state = ComboBoxState.Hot;
86+
}
8587

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

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

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-
}
110+
if (Application.RenderWithVisualStyles & _useComboBoxTheme)
111+
{
112+
RenderComboBoxButtonWithVisualStyles(pevent, state);
111113
}
114+
}
112115
#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.
116+
private void RenderComboBoxButtonWithVisualStyles(PaintEventArgs pevent, ComboBoxState state)
117+
{
118+
Rectangle dropDownButtonRect = new(0, 0, Width, Height);
119+
120+
if (state == ComboBoxState.Normal)
121+
{
122+
pevent.Graphics.FillRectangle(
123+
SystemBrushes.Window,
124+
dropDownButtonRect);
125+
}
126+
127+
using (DeviceContextHdcScope hdc = new(pevent))
128+
{
129+
ComboBoxRenderer.DrawDropDownButtonForHandle(
130+
hdc,
131+
dropDownButtonRect,
132+
state,
133+
ScaleHelper.IsScalingRequirementMet ? HWNDInternal : HWND.Null);
134+
}
135+
136+
// Redraw focus cues.
137+
//
138+
// For consistency with other PropertyGrid buttons, i.e. those opening system dialogs ("..."), that
139+
// always show visual cues when focused, we need to do the same for this custom button, painted as
140+
// a ComboBox control part (drop-down).
141+
if (Focused)
142+
{
143+
dropDownButtonRect.Inflate(-1, -1);
144+
DrawFocusRectangle(
145+
pevent.Graphics,
146+
dropDownButtonRect,
147+
ForeColor,
148+
BackColor);
149+
}
113150
}
114151

115152
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
}

src/System.Windows.Forms/System/Windows/Forms/Rendering/ControlPaint.ModernControlButton.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@ namespace System.Windows.Forms;
66
public static unsafe partial class ControlPaint
77
{
88
[Flags]
9-
internal enum ModernControlButton
9+
internal enum ModernControlButtonStyle
1010
{
1111
Empty = 0x0,
1212
Up = 0x1,
1313
Down = 0x2,
1414
UpDown = 0x3,
1515
Right = 0x4,
1616
Left = 0x8,
17-
LeftRight = 0xC,
17+
RightLeft = 0xC,
1818
Ellipse = 0x10,
19+
OpenDropDown = 0x20,
1920
SingleBorder = 0x10000,
20-
RoundedBorder = 0x20000
21+
RoundedBorder = 0x20000,
2122
}
2223
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace System.Windows.Forms;
5+
6+
public static unsafe partial class ControlPaint
7+
{
8+
internal enum ModernControlButtonState
9+
{
10+
Normal,
11+
Disabled,
12+
Pressed,
13+
Hover
14+
}
15+
}

0 commit comments

Comments
 (0)