Skip to content

Commit 200bcec

Browse files
Fix issues based on latest CTI-Testing.
1 parent 0727f28 commit 200bcec

File tree

7 files changed

+85
-41
lines changed

7 files changed

+85
-41
lines changed

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

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ public unsafe partial class Control :
223223
private static readonly int s_ambientPropertiesServiceProperty = PropertyStore.CreateKey();
224224
private static readonly int s_dataContextProperty = PropertyStore.CreateKey();
225225

226-
private static readonly int s_currentDpiProperty = PropertyStore.CreateKey();
227-
private static readonly int s_formerDpiProperty = PropertyStore.CreateKey();
226+
private static readonly int s_deviceDpiInternal = PropertyStore.CreateKey();
227+
private static readonly int s_originalDeviceDpiInternal = PropertyStore.CreateKey();
228228

229229
private static bool s_needToLoadComCtl = true;
230230

@@ -312,7 +312,7 @@ internal Control(bool autoInstallSyncContext) : base()
312312
// correctly in the PropertyStore, and that it is not set to 0.
313313
// So, this is available before any Constructor of any inheriting control runs.
314314
Properties.AddValue(
315-
s_currentDpiProperty,
315+
s_deviceDpiInternal,
316316
ScaleHelper.InitialSystemDpi);
317317

318318
_window = new ControlNativeWindow(this);
@@ -424,13 +424,17 @@ public Control(Control? parent, string? text, int left, int top, int width, int
424424
}
425425

426426
/// <summary>
427-
/// Giving a derived control a chance to initialize its state before the
427+
/// Giving an internally derived control a chance to initialize its state before the
428428
/// constructor code runs. If the control needs to access the initial or former
429-
/// DeviceDPI, it can safely access the internal Properties CurrentDpi and
430-
/// FormerDpi at any time.
429+
/// DeviceDPI, it can safely access the internal properties <see cref="DeviceDpiInternal"/>
430+
/// and <see cref="OriginalDeviceDpiInternal"/> to retrieve the DPI value. This method
431+
/// replaces the original internal method <c>InitializeConstantsForInitialDpi</c>.
431432
/// </summary>
432433
private protected virtual void InitializeControl()
433434
{
435+
// If you need to provide changed initial DPI values for a derived control,
436+
// make sure you set them NOT in the constructor, but provide them here via
437+
// the internal properties DeviceDpiInternal and OriginalDeviceDpi.
434438
}
435439

436440
/// <summary>
@@ -443,33 +447,39 @@ internal int DeviceDpiInternal
443447
get
444448
{
445449
return Properties.GetValueOrDefault(
446-
s_currentDpiProperty,
450+
s_deviceDpiInternal,
447451
ScaleHelper.InitialSystemDpi);
448452
}
449453

450454
set
451455
{
452456
if (value != DeviceDpiInternal)
453457
{
454-
Properties.AddOrRemoveValue(s_currentDpiProperty, value, ScaleHelper.InitialSystemDpi);
458+
Properties.AddOrRemoveValue(
459+
s_deviceDpiInternal,
460+
value,
461+
ScaleHelper.InitialSystemDpi);
455462
}
456463
}
457464
}
458465

459-
internal int FormerDeviceDpi
466+
internal int OriginalDeviceDpiInternal
460467
{
461468
get
462469
{
463470
return Properties.GetValueOrDefault(
464-
s_formerDpiProperty,
471+
s_originalDeviceDpiInternal,
465472
DeviceDpiInternal);
466473
}
467474

468475
set
469476
{
470-
if (value != FormerDeviceDpi)
477+
if (value != OriginalDeviceDpiInternal)
471478
{
472-
Properties.AddOrRemoveValue(s_formerDpiProperty, value, DeviceDpiInternal);
479+
Properties.AddOrRemoveValue(
480+
s_originalDeviceDpiInternal,
481+
value,
482+
DeviceDpiInternal);
473483
}
474484
}
475485
}
@@ -5369,9 +5379,9 @@ Site is { } site
53695379
// We would need to get adornments metrics for both (old and new) Dpi in case application is in PerMonitorV2 mode and Dpi changed.
53705380
AdjustWindowRectExForControlDpi(ref adornmentsAfterDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle);
53715381

5372-
if (FormerDeviceDpi != DeviceDpiInternal && OsVersion.IsWindows10_1703OrGreater())
5382+
if (OriginalDeviceDpiInternal != DeviceDpiInternal && OsVersion.IsWindows10_1703OrGreater())
53735383
{
5374-
AdjustWindowRectExForDpi(ref adornmentsBeforeDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle, FormerDeviceDpi);
5384+
AdjustWindowRectExForDpi(ref adornmentsBeforeDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle, OriginalDeviceDpiInternal);
53755385
}
53765386
else
53775387
{
@@ -7401,7 +7411,7 @@ void HandleHighDpi()
74017411
int old = DeviceDpiInternal;
74027412
Font localFont = GetCurrentFontAndDpi(out int fontDpi);
74037413

7404-
Properties.AddOrRemoveValue(s_currentDpiProperty, (int)PInvoke.GetDpiForWindow(this));
7414+
Properties.AddOrRemoveValue(s_deviceDpiInternal, (int)PInvoke.GetDpiForWindow(this));
74057415
if (old == DeviceDpiInternal)
74067416
{
74077417
return;
@@ -9322,9 +9332,9 @@ internal virtual void RecreateHandleCore()
93229332

93239333
// Note that we need to take DarkMode theming into account at a different point in time
93249334
// than when we create the handle for the first time. The reason is that recreating the handle
9325-
// usually also recreates the handles of any child controls, and we want to
9335+
// often also recreates the handles of any child controls, and we want to
93269336
// ensure that the theming is applied to all child controls as well.
9327-
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
9337+
#pragma warning disable WFO5001
93289338
if (Application.IsDarkModeEnabled
93299339
&& GetStyle(ControlStyles.ApplyThemingImplicitly))
93309340
{
@@ -11511,7 +11521,7 @@ private void WmDpiChangedBeforeParent(ref Message m)
1151111521
{
1151211522
DefWndProc(ref m);
1151311523

11514-
Properties.AddOrRemoveValue(s_formerDpiProperty, DeviceDpiInternal);
11524+
OriginalDeviceDpiInternal = DeviceDpiInternal;
1151511525

1151611526
// In order to support tests, will be querying Dpi from the message first.
1151711527
int newDeviceDpi = (short)m.WParamInternal.LOWORD;
@@ -11522,14 +11532,14 @@ private void WmDpiChangedBeforeParent(ref Message m)
1152211532
newDeviceDpi = (int)PInvoke.GetDpiForWindow(this);
1152311533
}
1152411534

11525-
if (FormerDeviceDpi == newDeviceDpi)
11535+
if (OriginalDeviceDpiInternal == newDeviceDpi)
1152611536
{
1152711537
OnDpiChangedBeforeParent(EventArgs.Empty);
1152811538
return;
1152911539
}
1153011540

1153111541
Font localFont = GetCurrentFontAndDpi(out int fontDpi);
11532-
Properties.AddOrRemoveValue(s_currentDpiProperty, newDeviceDpi);
11542+
DeviceDpiInternal = newDeviceDpi;
1153311543

1153411544
if (fontDpi == DeviceDpiInternal)
1153511545
{
@@ -11539,7 +11549,7 @@ private void WmDpiChangedBeforeParent(ref Message m)
1153911549

1154011550
// If it is a container control that inherit Font and is scaled by parent, we simply scale Font
1154111551
// and wait for OnFontChangedEvent caused by its parent. Otherwise, we scale Font and trigger
11542-
// 'OnFontChanged' event explicitly. ex: winforms designer in VS.
11552+
// 'OnFontChanged' event explicitly. ex: WinForms designer in VS.
1154311553
ContainerControl? container = this as ContainerControl;
1154411554
bool isLocalFontSet = IsFontSet();
1154511555

@@ -11562,7 +11572,7 @@ private void WmDpiChangedBeforeParent(ref Message m)
1156211572
// This flag is reset when scaling is done on Container in "OnParentFontChanged".
1156311573
container?.IsDpiChangeScalingRequired = true;
1156411574

11565-
RescaleConstantsForDpi(FormerDeviceDpi, DeviceDpiInternal);
11575+
RescaleConstantsForDpi(OriginalDeviceDpiInternal, DeviceDpiInternal);
1156611576
}
1156711577

1156811578
OnDpiChangedBeforeParent(EventArgs.Empty);

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ protected override CreateParams CreateParams
113113
{
114114
CreateParams cp = base.CreateParams;
115115
cp.ClassName = PInvoke.WC_BUTTON;
116+
116117
if (GetStyle(ControlStyles.UserPaint))
117118
{
118119
cp.Style |= PInvoke.BS_OWNERDRAW;
@@ -150,24 +151,27 @@ public virtual DialogResult DialogResult
150151
/// <summary>
151152
/// Defines, whether the control is owner-drawn. Based on this,
152153
/// the UserPaint flags get set, which in turn makes it later
153-
/// a Win32 controls, which we wrap (OwnerDraw == false) or if we
154-
/// draw ourselves. If the user wants to opt out of DarkMode, we cannot
155-
/// force System-Painting for FlatStyle.Standard, so we need to know here
156-
/// and now.
154+
/// a Win32 controls, which we wrap (OwnerDraw == false) or not, and then
155+
/// we draw ourselves. If the user wants to opt out of DarkMode, we can no
156+
/// longer force (wrapping) System-Painting for FlatStyle.Standard, and we
157+
/// need this then also here and now, before CreateParams is called.
157158
/// </summary>
158159
private protected override bool OwnerDraw
159160
{
160161
get
161162
{
162163
if (Application.IsDarkModeEnabled
164+
163165
// The SystemRenderer cannot render images. So, we flip to our
164-
// own DarkMode renderer, if we need to render images, except if
165-
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
166-
// the user wants to opt out of implicit DarkMode rendering.
166+
// own DarkMode renderer, if we need to render images, except if...
167167
&& Image is null
168-
// And this only counts for FlatStyle.Standard. For the rest,
169-
// we're using specific renderers, which check themselves, if
170-
// they need to apply Light- or DarkMode.
168+
169+
// ...the user wants to opt out of implicit DarkMode rendering.
170+
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
171+
172+
// And all of this only counts for FlatStyle.Standard. For the
173+
// rest, we're using specific renderers anyway, which check
174+
// themselves on demand, if they need to apply Light- or DarkMode.
171175
&& FlatStyle == FlatStyle.Standard)
172176
{
173177
return false;

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,16 @@ 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);
7574

7675
// This class overrides GetPreferredSizeCore, let Control automatically cache the result
7776
SetExtendedState(ExtendedStates.UserPreferredSizeCache, true);
7877

78+
// Be aware that OwnerDraw is effective calling CreateParams which in turn can be overwritten
79+
// by derived classes. So, it's important to set certain flags already in CreateParams -
80+
// setting them in the derived control's constructor would already be too late!
7981
SetStyle(ControlStyles.UserMouse | ControlStyles.UserPaint, OwnerDraw);
8082

8183
SetFlag(FlagUseMnemonic, true);
@@ -259,6 +261,7 @@ protected override CreateParams CreateParams
259261
#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.
260262

261263
CreateParams cp = base.CreateParams;
264+
262265
if (!OwnerDraw)
263266
{
264267
// WS_EX_RIGHT overrides the BS_XXXX alignment styles

src/System.Windows.Forms/System/Windows/Forms/Controls/ListBoxes/ListBox.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public partial class ListBox : ListControl
8989
private int _itemsCount;
9090

9191
/// <summary>
92-
/// This value stores the array of custom tabstops in the listBox. the array should be populated by
92+
/// This value stores the array of custom TabStops in the listBox. the array should be populated by
9393
/// integers in a ascending order.
9494
/// </summary>
9595
private IntegerCollection? _customTabOffsets;
@@ -264,6 +264,10 @@ protected override CreateParams CreateParams
264264
{
265265
get
266266
{
267+
#pragma warning disable WFO5001
268+
SetStyle(ControlStyles.ApplyThemingImplicitly, true);
269+
#pragma warning restore WFO5001
270+
267271
CreateParams cp = base.CreateParams;
268272
cp.ClassName = PInvoke.WC_LISTBOX;
269273

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2593,7 +2593,7 @@ private void OnLayoutInternal(bool dividerOnly)
25932593
{
25942594
// PropertyGrid does a special handling on scaling and positioning its
25952595
// child controls. These are not scaled by their parent when Dpi/Font change.
2596-
if (FormerDeviceDpi != DeviceDpiInternal)
2596+
if (OriginalDeviceDpiInternal != DeviceDpiInternal)
25972597
{
25982598
RescaleConstants();
25992599
SetupToolbar(fullRebuild: true);

src/System.Windows.Forms/System/Windows/Forms/Controls/TextBox/TextBoxBase.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ public override bool AutoSize
269269
}
270270
}
271271

272+
#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.
272273
/// <summary>
273274
/// Gets or sets the background color of the control.
274275
/// </summary>
@@ -283,17 +284,22 @@ public override Color BackColor
283284
{
284285
return base.BackColor;
285286
}
286-
else if (ReadOnly)
287-
{
288-
return SystemColors.Control;
289-
}
290287
else
291288
{
292-
return SystemColors.Window;
289+
return ReadOnly
290+
291+
// If we're ReadOnly and in DarkMode, we are using a different background color.
292+
? Application.IsDarkModeEnabled
293+
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
294+
? SystemColors.ControlLight
295+
: SystemColors.Control
296+
: SystemColors.Window;
293297
}
294298
}
299+
295300
set => base.BackColor = value;
296301
}
302+
#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.
297303

298304
[Browsable(false)]
299305
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -930,9 +936,11 @@ public bool ReadOnly
930936
if (_textBoxFlags[s_readOnly] != value)
931937
{
932938
_textBoxFlags[s_readOnly] = value;
939+
933940
if (IsHandleCreated)
934941
{
935942
PInvokeCore.SendMessage(this, PInvokeCore.EM_SETREADONLY, (WPARAM)(BOOL)value);
943+
EnsureReadonlyBackgroundColor(value);
936944
}
937945

938946
OnReadOnlyChanged(EventArgs.Empty);
@@ -942,6 +950,19 @@ public bool ReadOnly
942950
}
943951
}
944952

953+
#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.
954+
private void EnsureReadonlyBackgroundColor(bool value)
955+
{
956+
// If we have no specifically defined back color, we set the back color in case we're in dark mode.
957+
if (Application.IsDarkModeEnabled
958+
&& GetStyle(ControlStyles.ApplyThemingImplicitly))
959+
{
960+
base.BackColor = value ? SystemColors.ControlLight : SystemColors.Window;
961+
Invalidate();
962+
}
963+
}
964+
#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.
965+
945966
[SRCategory(nameof(SR.CatPropertyChanged))]
946967
[SRDescription(nameof(SR.TextBoxBaseOnReadOnlyChangedDescr))]
947968
public event EventHandler? ReadOnlyChanged
@@ -1372,6 +1393,8 @@ protected override void OnHandleCreated(EventArgs e)
13721393
PInvokeCore.SendMessage(this, PInvokeCore.EM_SETMODIFY, (WPARAM)(BOOL)true);
13731394
}
13741395

1396+
EnsureReadonlyBackgroundColor(true);
1397+
13751398
if (_textBoxFlags[s_scrollToCaretOnHandleCreated])
13761399
{
13771400
ScrollToCaret();

src/System.Windows.Forms/System/Windows/Forms/Form.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4516,7 +4516,7 @@ protected virtual void OnDpiChanged(DpiChangedEventArgs e)
45164516
if (e.DeviceDpiNew != e.DeviceDpiOld)
45174517
{
45184518
CommonProperties.xClearAllPreferredSizeCaches(this);
4519-
FormerDeviceDpi = e.DeviceDpiOld;
4519+
OriginalDeviceDpiInternal = e.DeviceDpiOld;
45204520

45214521
// call any additional handlers
45224522
((DpiChangedEventHandler?)Events[s_dpiChangedEvent])?.Invoke(this, e);

0 commit comments

Comments
 (0)