Skip to content

Commit 1528a35

Browse files
Address more review feedback.
1 parent df8badc commit 1528a35

File tree

8 files changed

+110
-90
lines changed

8 files changed

+110
-90
lines changed

src/System.Windows.Forms/System/Windows/Forms/Control.cs

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7329,18 +7329,6 @@ protected virtual void OnHandleCreated(EventArgs e)
73297329
SetWindowFont();
73307330
}
73317331

7332-
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
7333-
if (Application.IsDarkModeEnabled && GetStyle(ControlStyles.ApplyThemingImplicitly))
7334-
{
7335-
HRESULT result = PInvoke.SetWindowTheme(
7336-
hwnd: HWND,
7337-
pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}",
7338-
pszSubIdList: null);
7339-
7340-
Debug.Assert(result.Succeeded, "SetWindowTheme failed with HRESULT: " + result);
7341-
}
7342-
#pragma warning restore WFO5001
7343-
73447332
HandleHighDpi();
73457333

73467334
// Restore drag drop status. Ole Initialize happens when the ThreadContext in Application is created.
@@ -9338,26 +9326,11 @@ internal virtual void RecreateHandleCore()
93389326
}
93399327
}
93409328

9341-
// Note, that we need to take DarkMode-theming into account here at a different point in time
9342-
// compared to when we create the handle for the first time. The reason is that recreating the handle
9343-
// usually also might recreating the
9344-
9345-
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
9346-
if (Application.IsDarkModeEnabled
9347-
&& GetStyle(ControlStyles.ApplyThemingImplicitly))
9348-
{
9349-
_ = PInvoke.SetWindowTheme(
9350-
hwnd: HWND,
9351-
pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}",
9352-
pszSubIdList: null);
9353-
}
9354-
#pragma warning restore WFO5001
9355-
93569329
// Note that we need to take DarkMode theming into account at a different point in time
93579330
// than when we create the handle for the first time. The reason is that recreating the handle
93589331
// usually also recreates the handles of any child controls, and we want to
93599332
// ensure that the theming is applied to all child controls as well.
9360-
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
9333+
#pragma warning disable WFO5001
93619334
if (Application.IsDarkModeEnabled
93629335
&& GetStyle(ControlStyles.ApplyThemingImplicitly))
93639336
{

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

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -149,27 +149,25 @@ public virtual DialogResult DialogResult
149149
}
150150

151151
#pragma warning disable WFO5001
152-
/// <summary>
153-
/// Defines, whether the control is owner-drawn. Based on this,
154-
/// the UserPaint flags get set, which in turn makes it later
155-
/// a Win32 controls, which we wrap (OwnerDraw == false) or if we
156-
/// draw ourselves. If the user wants to opt out of DarkMode, we cannot
157-
/// force System-Painting for FlatStyle.Standard, so we need to know here
158-
/// and now.
159-
/// </summary>
152+
153+
// Indicates whether this control uses owner drawing, enabling UserPaint and determining
154+
// if we wrap the native Win32 control (OwnerDraw == false) or render it ourselves.
155+
// Also needed to detect a Dark Mode opt-out for FlatStyle.Standard when system painting
156+
// cannot be forced.
160157
private protected override bool OwnerDraw
161158
{
162159
get
163160
{
164161
if (Application.IsDarkModeEnabled
165-
// The SystemRenderer cannot render images. So, we flip to our
166-
// own DarkMode renderer, if we need to render images, except if
162+
163+
// SystemRenderer cannot draw images, so use our DarkMode renderer when images are needed,
164+
// unless implicit theming has been disabled.
167165
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
168-
// the user wants to opt out of implicit DarkMode rendering.
166+
167+
// Only apply when no image is set to avoid custom rendering without content.
169168
&& Image is null
170-
// And this only counts for FlatStyle.Standard. For the rest,
171-
// we're using specific renderers, which check themselves, if
172-
// they need to apply Light- or DarkMode.
169+
170+
// Restrict this logic to FlatStyle.Standard; other styles use their own light/dark checks.
173171
&& FlatStyle == FlatStyle.Standard)
174172
{
175173
return false;

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ protected ButtonBase()
6868
| ControlStyles.Opaque
6969
| ControlStyles.ResizeRedraw
7070
| ControlStyles.OptimizedDoubleBuffer
71-
// We gain about 2% in painting by avoiding extra GetWindowText calls
7271
| ControlStyles.CacheText
7372
| ControlStyles.StandardClick,
7473
true);
@@ -399,6 +398,7 @@ public FlatStyle FlatStyle
399398
_flatStyle = value;
400399
LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.FlatStyle);
401400
Invalidate();
401+
402402
UpdateOwnerDraw();
403403
}
404404
}
@@ -452,13 +452,18 @@ public Image? Image
452452
StopAnimate();
453453

454454
_image = value;
455+
455456
if (_image is not null)
456457
{
457458
ImageIndex = ImageList.Indexer.DefaultIndex;
458459
ImageList = null;
459460
}
460461

462+
// If we have an Image, for some flat styles we need to change the rendering approach from
463+
// being a wrapper around the Win32 control to being owner-drawn. The Win32 control does not
464+
// support images in the same flexible way as we need it.
461465
UpdateOwnerDraw();
466+
462467
LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.Image);
463468
Animate();
464469
Invalidate();
@@ -1015,7 +1020,6 @@ internal override Size GetPreferredSizeCore(Size proposedConstraints)
10151020
return LayoutUtils.UnionSizes(preferredSize + Padding.Size, MinimumSize);
10161021
}
10171022

1018-
#pragma warning disable WFO5001
10191023
/// <summary>
10201024
/// Returns an adapter for Rendering one of the FlatStyles. Note, that we always render
10211025
/// buttons ourselves, except when the User explicitly requests FlatStyle.System rendering!
@@ -1045,7 +1049,6 @@ internal ButtonBaseAdapter Adapter
10451049

10461050
_cachedAdapterType = FlatStyle;
10471051
}
1048-
#pragma warning restore WFO5001
10491052

10501053
return _adapter;
10511054
}
@@ -1295,6 +1298,10 @@ private void SetFlag(int flag, bool value)
12951298

12961299
private bool ShouldSerializeImage() => _image is not null;
12971300

1301+
// Indicates whether this control uses owner drawing, enabling UserPaint and determining
1302+
// if we wrap the native Win32 control (OwnerDraw == false) or render it ourselves.
1303+
// Also needed to detect a Dark Mode opt-out for FlatStyle.Standard when system painting
1304+
// cannot be forced.
12981305
private protected void UpdateOwnerDraw()
12991306
{
13001307
if (OwnerDraw != GetStyle(ControlStyles.UserPaint))

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

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ public CheckBox() : base()
4949
TextAlign = ContentAlignment.MiddleLeft;
5050
}
5151

52-
protected override void InitializeControl(int deviceDpi) => ScaleConstants();
52+
protected override void InitializeControl(int deviceDpi)
53+
{
54+
base.InitializeControl(deviceDpi);
55+
ScaleConstants();
56+
}
5357

5458
private bool AccObjDoDefaultAction { get; set; }
5559

@@ -83,9 +87,8 @@ public Appearance Appearance
8387
{
8488
UpdateStyles();
8589

86-
// UpdateStyles should also update the UserDraw flag, but it doesn't.
87-
// Since we hijack FlatStyle.Standard for DarkMode and let that SystemRender,
88-
// the threshold from Normal to Button is important to update the OwnerDraw flag.
90+
// When we change the appearance, we might also either stop or start wrapping the
91+
// win32 control. This is controlled by the OwnerDraw setting.
8992
UpdateOwnerDraw();
9093
}
9194

@@ -95,19 +98,36 @@ public Appearance Appearance
9598
}
9699

97100
#pragma warning disable WFO5001
101+
// Indicates whether this control uses owner drawing, enabling UserPaint and determining
102+
// if we wrap the native Win32 control (OwnerDraw == false) or render it ourselves.
103+
// Also needed to detect a Dark Mode opt-out for FlatStyle.Standard when system painting
104+
// cannot be forced.
105+
private protected override bool OwnerDraw
106+
{
107+
get
108+
{
109+
if (Application.IsDarkModeEnabled
110+
111+
// We need wrapping around the Win32 control only for
112+
// when we're dealing with a button experience.
113+
&& Appearance == Appearance.Button
114+
115+
// SystemRenderer cannot draw images, so use our DarkMode renderer
116+
// when images are needed, unless implicit theming has been disabled.
117+
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
98118

99-
private protected override bool OwnerDraw =>
100-
// We want NO owner draw ONLY when we're
101-
// * In Dark Mode
102-
// * When _then_ the Appearance is Button
103-
// * But then ONLY when we're rendering with FlatStyle.Standard
104-
// (because that would let us usually let us draw with the VisualStyleRenderers,
105-
// which cause HighDPI issues in Dark Mode).
106-
(!Application.IsDarkModeEnabled
107-
|| Appearance != Appearance.Button
108-
|| FlatStyle != FlatStyle.Standard)
109-
&& base.OwnerDraw;
119+
// Only apply when no image is set to avoid custom rendering without content.
120+
&& Image is null
110121

122+
// Restrict this logic to FlatStyle.Standard; other styles use their own light/dark checks.
123+
&& FlatStyle == FlatStyle.Standard)
124+
{
125+
return false;
126+
}
127+
128+
return base.OwnerDraw;
129+
}
130+
}
111131
#pragma warning restore WFO5001
112132

113133
[SRCategory(nameof(SR.CatPropertyChanged))]
@@ -247,6 +267,7 @@ protected override CreateParams CreateParams
247267
{
248268
CreateParams cp = base.CreateParams;
249269
cp.ClassName = PInvoke.WC_BUTTON;
270+
250271
if (OwnerDraw)
251272
{
252273
cp.Style |= PInvoke.BS_OWNERDRAW;
@@ -261,6 +282,7 @@ protected override CreateParams CreateParams
261282

262283
// Determine the alignment of the check box
263284
ContentAlignment align = RtlTranslateContent(CheckAlign);
285+
264286
if ((align & AnyRight) != 0)
265287
{
266288
cp.Style |= PInvoke.BS_RIGHTBUTTON;

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

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ public RadioButton() : base()
5050
SetAutoSizeMode(AutoSizeMode.GrowAndShrink);
5151
}
5252

53+
protected override void InitializeControl(int deviceDpi)
54+
{
55+
base.InitializeControl(deviceDpi);
56+
ScaleConstants();
57+
}
58+
5359
/// <summary>
5460
/// Gets or sets a value indicating whether the <see cref="Checked"/> value and the appearance of
5561
/// the control automatically change when the control is clicked.
@@ -97,7 +103,8 @@ public Appearance Appearance
97103
{
98104
UpdateStyles();
99105

100-
// UpdateStyles should also update the UserDraw flag, but it doesn't.
106+
// When we change the appearance, we might also either stop or start wrapping the
107+
// win32 control. This is controlled by the OwnerDraw setting.
101108
UpdateOwnerDraw();
102109
}
103110

@@ -172,18 +179,36 @@ public bool Checked
172179
}
173180

174181
#pragma warning disable WFO5001
175-
private protected override bool OwnerDraw =>
176-
// Order is key here - do NOT change!
177-
// We want NO owner draw ONLY when we're
178-
// * in Dark Mode
179-
// * when _then_ the Appearance is Button
180-
// * but then ONLY when we're rendering with FlatStyle.Standard
181-
// (because that would let us usually let us draw with the VisualStyleRenderers,
182-
// which cause HighDPI issues in Dark Mode).
183-
(!Application.IsDarkModeEnabled
184-
|| Appearance != Appearance.Button
185-
|| FlatStyle != FlatStyle.Standard)
186-
&& base.OwnerDraw;
182+
// Indicates whether this control uses owner drawing, enabling UserPaint and determining
183+
// if we wrap the native Win32 control (OwnerDraw == false) or render it ourselves.
184+
// Also needed to detect a Dark Mode opt-out for FlatStyle.Standard when system painting
185+
// cannot be forced.
186+
private protected override bool OwnerDraw
187+
{
188+
get
189+
{
190+
if (Application.IsDarkModeEnabled
191+
192+
// We need wrapping around the Win32 control only for
193+
// when we're dealing with a button experience.
194+
&& Appearance == Appearance.Button
195+
196+
// SystemRenderer cannot draw images, so use our DarkMode renderer
197+
// when images are needed, unless implicit theming has been disabled.
198+
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
199+
200+
// Only apply when no image is set to avoid custom rendering without content.
201+
&& Image is null
202+
203+
// Restrict this logic to FlatStyle.Standard; other styles use their own light/dark checks.
204+
&& FlatStyle == FlatStyle.Standard)
205+
{
206+
return false;
207+
}
208+
209+
return base.OwnerDraw;
210+
}
211+
}
187212
#pragma warning restore WFO5001
188213

189214
/// <hideinheritance/>
@@ -210,6 +235,7 @@ protected override CreateParams CreateParams
210235
{
211236
CreateParams cp = base.CreateParams;
212237
cp.ClassName = PInvoke.WC_BUTTON;
238+
213239
if (OwnerDraw)
214240
{
215241
cp.Style |= PInvoke.BS_OWNERDRAW;
@@ -259,8 +285,6 @@ private void ScaleConstants()
259285
_flatSystemStyleMinimumHeight = LogicalToDeviceUnits(LogicalFlatSystemStyleMinimumHeight);
260286
}
261287

262-
protected override void InitializeControl(int deviceDpi) => ScaleConstants();
263-
264288
internal override Size GetPreferredSizeCore(Size proposedConstraints)
265289
{
266290
if (FlatStyle != FlatStyle.System)

src/System.Windows.Forms/System/Windows/Forms/Controls/TabControl/TabControl.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2022,7 +2022,6 @@ private unsafe void WmTabBaseReLayout()
20222022

20232023
// Remove other TabBaseReLayout messages from the message queue
20242024
MSG msg = default;
2025-
20262025
while (PInvokeCore.PeekMessage(
20272026
&msg,
20282027
this,

src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStripRenderer.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -666,26 +666,26 @@ private protected Point[] RenderArrowCore(
666666
ArrowDirection.Up =>
667667
[
668668
new(middle.X - Offset2X, middle.Y + 1),
669-
new(middle.X + Offset2X + 1, middle.Y + 1),
670-
new(middle.X, middle.Y - Offset2Y)
669+
new(middle.X + Offset2X + 1, middle.Y + 1),
670+
new(middle.X, middle.Y - Offset2Y)
671671
],
672672
ArrowDirection.Left =>
673673
[
674674
new(middle.X + Offset2X, middle.Y - s_offset4Y),
675-
new(middle.X + Offset2X, middle.Y + s_offset4Y),
676-
new(middle.X - horizontalOffset, middle.Y)
675+
new(middle.X + Offset2X, middle.Y + s_offset4Y),
676+
new(middle.X - horizontalOffset, middle.Y)
677677
],
678678
ArrowDirection.Right =>
679679
[
680680
new(middle.X - Offset2X, middle.Y - s_offset4Y),
681-
new(middle.X - Offset2X, middle.Y + s_offset4Y),
682-
new(middle.X + horizontalOffset, middle.Y)
681+
new(middle.X - Offset2X, middle.Y + s_offset4Y),
682+
new(middle.X + horizontalOffset, middle.Y)
683683
],
684684
_ =>
685685
[
686686
new(middle.X - Offset2X, middle.Y - 1),
687-
new(middle.X + Offset2X + 1, middle.Y - 1),
688-
new(middle.X, middle.Y + Offset2Y)
687+
new(middle.X + Offset2X + 1, middle.Y - 1),
688+
new(middle.X, middle.Y + Offset2Y)
689689
],
690690
};
691691

@@ -1121,18 +1121,18 @@ private protected static void OnRenderStatusStripSizingGrip(
11211121
// Default values
11221122
(int offset, Rectangle rect) cornerDef = (2, new(1, 1, 2, 2));
11231123

1124+
#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.
11241125
if (Environment.OSVersion.Version >= new Version(10, 0, 22000)
11251126
&& statusStrip.FindForm() is Form f)
11261127
{
1127-
#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.
11281128
cornerDef = f.FormCornerPreference switch
11291129
{
11301130
FormCornerPreference.Round => (4, new(1, 1, 2, 2)),
11311131
FormCornerPreference.RoundSmall => (3, new(1, 1, 2, 2)),
11321132
_ => (2, new(0, 0, 2, 2))
11331133
};
1134-
#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.
11351134
}
1135+
#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.
11361136

11371137
return cornerDef;
11381138
}

0 commit comments

Comments
 (0)