Skip to content

Introduce the new API InitializeControl on Control #13551

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions src/System.Windows.Forms/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
#nullable enable
[WFO5001]static System.Windows.Forms.Application.SetColorMode(System.Windows.Forms.SystemColorMode systemColorMode) -> void
[WFO5001]System.Windows.Forms.ControlStyles.ApplyThemingImplicitly = 524288 -> System.Windows.Forms.ControlStyles
[WFO5001]System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.Form.FormCaptionTextColorChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.Form.FormCornerPreferenceChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.Default = 0 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.DoNotRound = 1 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.Round = 2 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.RoundSmall = 3 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.SystemColorMode
[WFO5001]System.Windows.Forms.SystemColorMode.Classic = 0 -> System.Windows.Forms.SystemColorMode
[WFO5001]System.Windows.Forms.SystemColorMode.Dark = 2 -> System.Windows.Forms.SystemColorMode
[WFO5001]System.Windows.Forms.SystemColorMode.System = 1 -> System.Windows.Forms.SystemColorMode
[WFO5001]virtual System.Windows.Forms.Form.OnFormBorderColorChanged(System.EventArgs! e) -> void
[WFO5001]virtual System.Windows.Forms.Form.OnFormCaptionBackColorChanged(System.EventArgs! e) -> void
[WFO5001]virtual System.Windows.Forms.Form.OnFormCaptionTextColorChanged(System.EventArgs! e) -> void
[WFO5001]virtual System.Windows.Forms.Form.OnFormCornerPreferenceChanged(System.EventArgs! e) -> void
[WFO5002]static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task<System.Windows.Forms.TaskDialogButton!>!
[WFO5002]static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task<System.Windows.Forms.TaskDialogButton!>!
[WFO5002]static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterScreen) -> System.Threading.Tasks.Task<System.Windows.Forms.TaskDialogButton!>!
Expand Down
15 changes: 15 additions & 0 deletions src/System.Windows.Forms/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
override System.Windows.Forms.DataGridView.CreateParams.get -> System.Windows.Forms.CreateParams!
override System.Windows.Forms.TabPage.CreateParams.get -> System.Windows.Forms.CreateParams!
System.Windows.Forms.Form.FormScreenCaptureMode.get -> System.Windows.Forms.ScreenCaptureMode
System.Windows.Forms.Form.FormScreenCaptureMode.set -> void
System.Windows.Forms.ScreenCaptureMode
System.Windows.Forms.ScreenCaptureMode.Allow = 0 -> System.Windows.Forms.ScreenCaptureMode
System.Windows.Forms.ScreenCaptureMode.HideContent = 1 -> System.Windows.Forms.ScreenCaptureMode
System.Windows.Forms.ScreenCaptureMode.HideWindow = 2 -> System.Windows.Forms.ScreenCaptureMode
[WFO5001]System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.Default = 0 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.DoNotRound = 1 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.Round = 2 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.FormCornerPreference.RoundSmall = 3 -> System.Windows.Forms.FormCornerPreference
[WFO5001]System.Windows.Forms.Form.FormBorderColorChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.Form.FormCaptionBackColorChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.Form.FormCaptionTextColorChanged -> System.EventHandler?
[WFO5001]System.Windows.Forms.Form.FormCornerPreferenceChanged -> System.EventHandler?
[WFO5001]virtual System.Windows.Forms.Form.OnFormBorderColorChanged(System.EventArgs! e) -> void
[WFO5001]virtual System.Windows.Forms.Form.OnFormCaptionBackColorChanged(System.EventArgs! e) -> void
[WFO5001]virtual System.Windows.Forms.Form.OnFormCaptionTextColorChanged(System.EventArgs! e) -> void
[WFO5001]virtual System.Windows.Forms.Form.OnFormCornerPreferenceChanged(System.EventArgs! e) -> void
157 changes: 112 additions & 45 deletions src/System.Windows.Forms/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,11 @@ public unsafe partial class Control :
private static readonly int s_cacheTextCountProperty = PropertyStore.CreateKey();
private static readonly int s_cacheTextFieldProperty = PropertyStore.CreateKey();
private static readonly int s_ambientPropertiesServiceProperty = PropertyStore.CreateKey();

private static readonly int s_dataContextProperty = PropertyStore.CreateKey();

private static readonly int s_currentDpiProperty = PropertyStore.CreateKey();
private static readonly int s_formerDpiProperty = PropertyStore.CreateKey();

private static bool s_needToLoadComCtl = true;

// This switch determines the default text rendering engine to use by some controls that support switching rendering engine.
Expand Down Expand Up @@ -267,8 +269,6 @@ public unsafe partial class Control :
private short _updateCount;
private LayoutEventArgs? _cachedLayoutEventArgs;
private Queue<ThreadMethodEntry>? _threadCallbackList;
internal int _deviceDpi;
internal int _oldDeviceDpi;

// For keeping track of our ui state for focus and keyboard cues. Using a member
// variable here because we hit this a lot
Expand Down Expand Up @@ -307,8 +307,14 @@ internal Control(bool autoInstallSyncContext) : base()
{
Properties = new PropertyStore();

// Initialize Dpi to the value on the primary screen, we will have the correct value when the Handle is created.
_deviceDpi = _oldDeviceDpi = ScaleHelper.InitialSystemDpi;
// Initialize Dpi to the value on the primary screen, we will have the correct
// 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(
s_currentDpiProperty,
ScaleHelper.InitialSystemDpi);

_window = new ControlNativeWindow(this);
RequiredScalingEnabled = true;
RequiredScaling = BoundsSpecified.All;
Expand All @@ -326,16 +332,21 @@ internal Control(bool autoInstallSyncContext) : base()
| ControlStyles.Selectable,
true);

// Allows inheriting controls to initialize their control-specific state, as well as
// perform DPI-depending initialization of fields before the constructor code of the
// respective (inheriting) control has a chance to run.
// At this point, the control is not yet created, but every necessary state as well as
// the base class properties are in place, because they have been initialized by the
// static ctor of Control, which is called before all other instance constructors.
InitializeControl();

// We baked the "default default" margin and min size into CommonProperties
// so that in the common case the PropertyStore would be empty. If, however,
// someone overrides these Default* methods, we need to write the default
// value into the PropertyStore in the ctor.

// Changing the order of property accesses here can break existing code as these are all virtual properties.
// Try to keep observable state for Control unchanged in this constructor to avoid nasty subtle bugs.

InitializeConstantsForInitialDpi(_deviceDpi);

if (DefaultMargin != CommonProperties.DefaultMargin)
{
Margin = DefaultMargin;
Expand Down Expand Up @@ -389,7 +400,8 @@ public Control(string? text) : this(null, text)
/// <summary>
/// Initializes a new instance of the <see cref="Control"/> class.
/// </summary>
public Control(string? text, int left, int top, int width, int height) : this(null, text, left, top, width, height)
public Control(string? text, int left, int top, int width, int height)
: this(null, text, left, top, width, height)
{
}

Expand All @@ -411,11 +423,57 @@ public Control(Control? parent, string? text, int left, int top, int width, int
Size = new Size(width, height);
}

/// <summary>
/// Giving a derived control a chance to initialize its state before the
/// constructor code runs. If the control needs to access the initial or former
/// DeviceDPI, it can safely access the internal Properties CurrentDpi and
/// FormerDpi at any time.
/// </summary>
private protected virtual void InitializeControl()
{
}

/// <summary>
/// Gets control Dpi awareness context value.
/// </summary>
internal DPI_AWARENESS_CONTEXT DpiAwarenessContext => _window.DpiAwarenessContext;

internal int DeviceDpiInternal
{
get
{
return Properties.GetValueOrDefault(
s_currentDpiProperty,
ScaleHelper.InitialSystemDpi);
}

set
{
if (value != DeviceDpiInternal)
{
Properties.AddOrRemoveValue(s_currentDpiProperty, value, ScaleHelper.InitialSystemDpi);
}
}
}

internal int FormerDeviceDpi
{
get
{
return Properties.GetValueOrDefault(
s_formerDpiProperty,
DeviceDpiInternal);
}

set
{
if (value != FormerDeviceDpi)
{
Properties.AddOrRemoveValue(s_formerDpiProperty, value, DeviceDpiInternal);
}
}
}

/// <summary>
/// The Accessibility Object for this Control
/// </summary>
Expand Down Expand Up @@ -1602,7 +1660,7 @@ public static Font DefaultFont
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int DeviceDpi
// deviceDpi may change in WmDpiChangedBeforeParent in PmV2 scenarios, so we can't cache statically.
=> ScaleHelper.IsThreadPerMonitorV2Aware ? _deviceDpi : ScaleHelper.InitialSystemDpi;
=> ScaleHelper.IsThreadPerMonitorV2Aware ? DeviceDpiInternal : ScaleHelper.InitialSystemDpi;

// The color to use when drawing disabled text. Normally we use BackColor,
// but that obviously won't work if we're transparent.
Expand Down Expand Up @@ -5311,9 +5369,9 @@ Site is { } site
// We would need to get adornments metrics for both (old and new) Dpi in case application is in PerMonitorV2 mode and Dpi changed.
AdjustWindowRectExForControlDpi(ref adornmentsAfterDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle);

if (_oldDeviceDpi != _deviceDpi && OsVersion.IsWindows10_1703OrGreater())
if (FormerDeviceDpi != DeviceDpiInternal && OsVersion.IsWindows10_1703OrGreater())
{
AdjustWindowRectExForDpi(ref adornmentsBeforeDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle, _oldDeviceDpi);
AdjustWindowRectExForDpi(ref adornmentsBeforeDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle, FormerDeviceDpi);
}
else
{
Expand Down Expand Up @@ -5466,7 +5524,7 @@ internal Control[] GetChildControlsInTabOrder(bool handleCreatedOnly)
/// <returns>The control's <see cref="Font"/></returns>
private Font GetCurrentFontAndDpi(out int fontDpi)
{
fontDpi = _deviceDpi;
fontDpi = DeviceDpiInternal;

// If application is in PerMonitorV2 mode and font is scaled when moved between monitors.
if (ScaledControlFont is not null)
Expand Down Expand Up @@ -6367,7 +6425,7 @@ public void ScaleBitmapLogicalToDevice(ref Bitmap logicalBitmap)

private protected void AdjustWindowRectExForControlDpi(ref RECT rect, WINDOW_STYLE style, bool bMenu, WINDOW_EX_STYLE exStyle)
{
AdjustWindowRectExForDpi(ref rect, style, bMenu, exStyle, _deviceDpi);
AdjustWindowRectExForDpi(ref rect, style, bMenu, exStyle, DeviceDpiInternal);
}

private static void AdjustWindowRectExForDpi(ref RECT rect, WINDOW_STYLE style, bool bMenu, WINDOW_EX_STYLE exStyle, int dpi)
Expand Down Expand Up @@ -7297,16 +7355,6 @@ protected virtual void OnHandleCreated(EventArgs e)
PInvoke.SetWindowText(this, _text);
}

#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
if (Application.IsDarkModeEnabled && GetStyle(ControlStyles.ApplyThemingImplicitly))
{
_ = PInvoke.SetWindowTheme(
hwnd: HWND,
pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}",
pszSubIdList: null);
}
#pragma warning restore WFO5001

if (this is not ScrollableControl
&& !IsMirrored
&& GetExtendedState(ExtendedStates.SetScrollPosition)
Expand All @@ -7316,6 +7364,18 @@ protected virtual void OnHandleCreated(EventArgs e)
SetExtendedState(ExtendedStates.HaveInvoked, true);
SetExtendedState(ExtendedStates.SetScrollPosition, false);
}

#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
if (Application.IsDarkModeEnabled
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
&& !RecreatingHandle)
{
_ = PInvoke.SetWindowTheme(
hwnd: HWND,
pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}",
pszSubIdList: null);
}
#pragma warning restore WFO5001
}

((EventHandler?)Events[s_handleCreatedEvent])?.Invoke(this, e);
Expand All @@ -7338,19 +7398,20 @@ void HandleHighDpi()
return;
}

int old = _deviceDpi;
int old = DeviceDpiInternal;
Font localFont = GetCurrentFontAndDpi(out int fontDpi);
_deviceDpi = (int)PInvoke.GetDpiForWindow(this);
if (old == _deviceDpi)

Properties.AddOrRemoveValue(s_currentDpiProperty, (int)PInvoke.GetDpiForWindow(this));
if (old == DeviceDpiInternal)
{
return;
}

if (fontDpi != _deviceDpi)
if (fontDpi != DeviceDpiInternal)
{
// Controls are by default font scaled.
// Dpi change requires font to be recalculated in order to get controls scaled with right dpi.
Font fontForDpi = GetScaledFont(localFont, _deviceDpi, fontDpi);
Font fontForDpi = GetScaledFont(localFont, DeviceDpiInternal, fontDpi);
ScaledControlFont = fontForDpi;

// If it is a container control that inherit Font and is scaled by parent, we simply scale Font
Expand All @@ -7362,7 +7423,7 @@ void HandleHighDpi()
}
}

RescaleConstantsForDpi(old, _deviceDpi);
RescaleConstantsForDpi(old, DeviceDpiInternal);

// If the control is top-level window and its StartPosition is not WindowsDefaultLocation, Location needs
// recalculated. For example, a Form centered as FormStartPosition.CenterParent or FormStartPosition.CenterScreen,
Expand Down Expand Up @@ -8035,15 +8096,6 @@ protected virtual void OnValidated(EventArgs e)
((EventHandler?)Events[s_validatedEvent])?.Invoke(this, e);
}

/// <summary>
/// This is called in the <see cref="Control"/> constructor before calculating the initial <see cref="Size"/>.
/// This gives a chance to initialize fields that will be used in calls to sizing related virtuals such as
/// <see cref="DefaultSize"/>, etc. The real size cannot be calculated until the handle is created as Windows
/// can have their own DPI setting. When the handle is created, <see cref="RescaleConstantsForDpi(int, int)"/>
/// is called.
/// </summary>
private protected virtual void InitializeConstantsForInitialDpi(int initialDpi) { }

/// <summary>
/// Invoked when the control handle is created and right before the top level parent control receives a
/// WM_DPICHANGED message.
Expand Down Expand Up @@ -9268,6 +9320,21 @@ internal virtual void RecreateHandleCore()
}
}

// Note that we need to take DarkMode theming into account at a different point in time
// than when we create the handle for the first time. The reason is that recreating the handle
// usually also recreates the handles of any child controls, and we want to
// ensure that the theming is applied to all child controls as well.
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
if (Application.IsDarkModeEnabled
&& GetStyle(ControlStyles.ApplyThemingImplicitly))
{
_ = PInvoke.SetWindowTheme(
hwnd: HWND,
pszSubAppName: $"{DarkModeIdentifier}_{ExplorerThemeIdentifier}",
pszSubIdList: null);
}
#pragma warning restore WFO5001

// Restore control focus
if (focused)
{
Expand Down Expand Up @@ -11444,7 +11511,7 @@ private void WmDpiChangedBeforeParent(ref Message m)
{
DefWndProc(ref m);

_oldDeviceDpi = _deviceDpi;
Properties.AddOrRemoveValue(s_formerDpiProperty, DeviceDpiInternal);

// In order to support tests, will be querying Dpi from the message first.
int newDeviceDpi = (short)m.WParamInternal.LOWORD;
Expand All @@ -11455,16 +11522,16 @@ private void WmDpiChangedBeforeParent(ref Message m)
newDeviceDpi = (int)PInvoke.GetDpiForWindow(this);
}

if (_oldDeviceDpi == newDeviceDpi)
if (FormerDeviceDpi == newDeviceDpi)
{
OnDpiChangedBeforeParent(EventArgs.Empty);
return;
}

Font localFont = GetCurrentFontAndDpi(out int fontDpi);
_deviceDpi = newDeviceDpi;
Properties.AddOrRemoveValue(s_currentDpiProperty, newDeviceDpi);

if (fontDpi == _deviceDpi)
if (fontDpi == DeviceDpiInternal)
{
OnDpiChangedBeforeParent(EventArgs.Empty);
return;
Expand All @@ -11476,7 +11543,7 @@ private void WmDpiChangedBeforeParent(ref Message m)
ContainerControl? container = this as ContainerControl;
bool isLocalFontSet = IsFontSet();

ScaledControlFont = GetScaledFont(localFont, _deviceDpi, fontDpi);
ScaledControlFont = GetScaledFont(localFont, DeviceDpiInternal, fontDpi);

if (isLocalFontSet || container is null || !IsScaledByParent(this))
{
Expand All @@ -11495,7 +11562,7 @@ private void WmDpiChangedBeforeParent(ref Message m)
// This flag is reset when scaling is done on Container in "OnParentFontChanged".
container?.IsDpiChangeScalingRequired = true;

RescaleConstantsForDpi(_oldDeviceDpi, _deviceDpi);
RescaleConstantsForDpi(FormerDeviceDpi, DeviceDpiInternal);
}

OnDpiChangedBeforeParent(EventArgs.Empty);
Expand Down
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy