-
Notifications
You must be signed in to change notification settings - Fork 1k
Refactor DarkMode management for Controls as per API Review outcome #13582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor DarkMode management for Controls as per API Review outcome #13582
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR reverts a previously unapproved architecture for DarkMode theming in controls and refactors DPI handling to use a PropertyStore‐based approach. Key changes include removing early SetStyle calls from constructors in favor of setting them within CreateParams overrides, replacing the removed InitializeConstantsForInitialDpi method with a standardized InitializeControl override, and migrating from internal DPI field usage to the new DeviceDpiInternal/FormerDeviceDpi properties.
Reviewed Changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
TabControl.cs, Splitter.cs, ProgressBar.cs, PictureBox.cs, MonthCalendar.cs, ListBox.cs, Label.cs | Moved the ApplyThemingImplicitly flag from the constructor to the CreateParams getter and removed redundant pragmas where needed. |
PropertyGrid.cs, DataGridView.cs, Control.cs | Updated DPI handling code to use the new PropertyStore keys and properties (DeviceDpiInternal and FormerDeviceDpi), and removed the obsolete InitializeConstantsForInitialDpi. |
DataGridViewColumn.cs, ComboBox.FlatComboAdapter.cs | Updated DPI retrieval calls to reference the new properties. |
RadioButton.cs, CheckBox.cs, ButtonBase.cs, Button.cs | Replaced the old DPI initialization methods with InitializeControl and changed the accessibility or virtuality of OwnerDraw where applicable. |
PublicAPI.Unshipped.txt, PublicAPI.Shipped.txt | Updated and removed API entries to align with the refactored theming and DPI handling changes. |
Comments suppressed due to low confidence (1)
src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs:620
- Changing the accessibility and making OwnerDraw virtual may affect how derived controls override this property. Verify that the new visibility and virtual nature align with the intended usage across the control hierarchy.
private protected virtual bool OwnerDraw => FlatStyle != FlatStyle.System;
src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs
Outdated
Show resolved
Hide resolved
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #13582 +/- ##
====================================================
+ Coverage 76.60070% 97.40072% +20.80002%
====================================================
Files 3236 1182 -2054
Lines 639344 352713 -286631
Branches 47314 5368 -41946
====================================================
- Hits 489742 343545 -146197
+ Misses 146026 8412 -137614
+ Partials 3576 756 -2820
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
a8a909a
to
53aaa95
Compare
53aaa95
to
b3118f9
Compare
67539e5
to
f7cbf17
Compare
src/System.Windows.Forms/System/Windows/Forms/Controls/TextBox/TextBoxBase.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing blocking, but the comments should be followed up on.
s_deviceDpiInternal, | ||
ScaleHelper.InitialSystemDpi); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: don't leave a blank line between getter and setter blocks, also these should be get =>
| ControlStyles.CacheText | ||
| ControlStyles.StandardClick, | ||
true); | ||
|
||
// This class overrides GetPreferredSizeCore, let Control automatically cache the result | ||
SetExtendedState(ExtendedStates.UserPreferredSizeCache, true); | ||
|
||
// Be aware that OwnerDraw is effective calling CreateParams which in turn can be overwritten | ||
// by derived classes. So, it's important to set certain flags already in CreateParams - | ||
// setting them in the derived control's constructor would already be too late! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: avoid exclamation points
// value when the Handle is created. We use the static infra to ensure that the DPI is set | ||
// correctly in the PropertyStore, and that it is not set to 0. | ||
// So, this is available before any Constructor of any inheriting control runs. | ||
Properties.AddValue( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we always create a value, putting it in the Properties has unnecessary overhead and as such the general recommendation is to not do this for things that aren't optional or are in inner loops.
I will merge this, so it will not get stale, and will follow up in the next PR. |
For a Control to indicate that it wants to participate in DarkMode theming, it needs to indicate that by setting the style flag
ApplyThemingImplicitly
.The original architectural plan was to introduce a dedicate protected virtual method
InitializeControl
that would be called by the constructor of Control, which would then set theApplyThemingImplicitly
flag and give also the control the chance to learn about the initial DeviceAPI, with that replacing the original internal method.This approach was not approved by the API Review board (see #13550 (comment)).
Consequently, this PR builds the DarkMode handling back to the original approach and also refactors the flaw of the approach how controls are internally provided the initial DPI setting.
(Also pointed outby the API Review.)
Because of the general architecture of the Controls inheritance hierarchy, Controls need to make sure to opt into (or opt out) DarkMode handling before their own constructor runs, which means they have to override
CreateParams
and set the respective flags before calling the base implementation.Microsoft Reviewers: Open in CodeFlow