Content-Length: 727124 | pFad | http://github.com/dotnet/winforms/pull/13551/files

2D Introduce the new API InitializeControl on Control by KlausLoeffelmann · Pull Request #13551 · dotnet/winforms · GitHub
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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
30 changes: 30 additions & 0 deletions src/System.Windows.Forms/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,33 @@ 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
override System.Windows.Forms.ButtonBase.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.CheckBox.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.DataGridView.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.Label.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.ListBox.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.MonthCalendar.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.PictureBox.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.ProgressBar.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.RadioButton.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.ScrollableControl.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.ScrollBar.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.Splitter.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.TabControl.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.TabPage.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.TrackBar.InitializeControl(int deviceDpi) -> void
override System.Windows.Forms.TreeView.InitializeControl(int deviceDpi) -> void
virtual System.Windows.Forms.Control.InitializeControl(int deviceDpi) -> void
[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
118 changes: 95 additions & 23 deletions src/System.Windows.Forms/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ internal Control(bool autoInstallSyncContext) : base()

// Initialize Dpi to the value on the primary screen, we will have the correct value when the Handle is created.
_deviceDpi = _oldDeviceDpi = ScaleHelper.InitialSystemDpi;

_window = new ControlNativeWindow(this);
RequiredScalingEnabled = true;
RequiredScaling = BoundsSpecified.All;
Expand All @@ -326,16 +327,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(_deviceDpi);

// 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 +395,8 @@ public Control(string? text) : this(null, text)
//github.com/ <summary>
//github.com/ Initializes a new instance of the <see cref="Control"/> class.
//github.com/ </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,6 +418,63 @@ public Control(Control? parent, string? text, int left, int top, int width, int
Size = new Size(width, height);
}

//github.com/ <summary>
//github.com/ Provides inheriting controls a dedicated early-initialization hook that is guaranteed to run
//github.com/ <i>before</i> <see cref="CreateParams"/> is called by any base class constructor.
//github.com/ This method enables derived controls to set styles, flags, or perform other setup that must
//github.com/ occur prior to any base class logic that depends on such initialization.
//github.com/ </summary>
//github.com/ <remarks>
//github.com/ <para>
//github.com/ In WinForms, the constructor call chain for controls can result in base class constructors
//github.com/ invoking methods such as <see cref="CreateParams"/> or other initialization logic
//github.com/ <b>before</b> the derived class's constructor body executes. This makes it impossible for
//github.com/ the derived class to perform certain setup in time using only its constructor.
//github.com/ </para>
//github.com/ <para>
//github.com/ For example, consider the following inheritance and call chain:
//github.com/ </para>
//github.com/ <code>
//github.com/ public class MyButton : Button
//github.com/ {
//github.com/ public MyButton()
//github.com/ {
//github.com/ // This code runs after Button's constructor, which may have already called CreateParams.
//github.com/ }
//github.com/
//github.com/ protected override void InitializeControl(int deviceDpi)
//github.com/ {
//github.com/ // This code runs before any base class calls to CreateParams or similar methods.
//github.com/ }
//github.com/ }
//github.com/
//github.com/ // Call chain:
//github.com/ new MyButton()
//github.com/ → Control..ctor()
//github.com/ → Control.InitializeControl (called in base constructor)
//github.com/ → ButtonBase.InitializeControl (called before CreateParams)
//github.com/ → Button.InitializeControl (called before CreateParams)
//github.com/ → MyButton.InitializeControl (called before CreateParams)
//github.com/ → Control.CreateParams (called in base constructor)
//github.com/ → ButtonBase.CreateParams (called before CreateParams)
//github.com/ → Button.CreateParams (called before CreateParams)
//github.com/ → MyButton.CreateParams (called before CreateParams)
//github.com/ → MyButton.CreateParams (called before CreateParams)
//github.com/ → ButtonBase..ctor()
//github.com/ → Button..ctor()
//github.com/ → MyButton..ctor()
//github.com/ </code>
//github.com/ <para>
//github.com/ By overriding <c>InitializeControl</c>, inheritors can ensure their initialization logic
//github.com/ runs at the correct time, even when base class constructors invoke methods that require
//github.com/ early setup.
//github.com/ </para>
//github.com/ </remarks>
//github.com/ <param name="deviceDpi">The DPI value for the control's device context.</param>
protected virtual void InitializeControl(int deviceDpi)
{
}

//github.com/ <summary>
//github.com/ Gets control Dpi awareness context value.
//github.com/ </summary>
Expand Down Expand Up @@ -7297,16 +7361,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 +7370,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 Down Expand Up @@ -8035,15 +8101,6 @@ protected virtual void OnValidated(EventArgs e)
((EventHandler?)Events[s_validatedEvent])?.Invoke(this, e);
}

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

//github.com/ <summary>
//github.com/ Invoked when the control handle is created and right before the top level parent control receives a
//github.com/ WM_DPICHANGED message.
Expand Down Expand Up @@ -9268,6 +9325,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
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,38 @@ public virtual DialogResult DialogResult
}
}

#pragma warning disable WFO5001
//github.com/ <summary>
//github.com/ Defines, whether the control is owner-drawn. Based on this,
//github.com/ the UserPaint flags get set, which in turn makes it later
//github.com/ a Win32 controls, which we wrap (OwnerDraw == false) or if we
//github.com/ draw ourselves. If the user wants to opt out of DarkMode, we cannot
//github.com/ force System-Painting for FlatStyle.Standard, so we need to know here
//github.com/ and now.
//github.com/ </summary>
private protected override bool OwnerDraw
{
get
{
if (Application.IsDarkModeEnabled
// The SystemRenderer cannot render images. So, we flip to our
// own DarkMode renderer, if we need to render images, except if
&& GetStyle(ControlStyles.ApplyThemingImplicitly)
// the user wants to opt out of implicit DarkMode rendering.
&& Image is null
// And this only counts for FlatStyle.Standard. For the rest,
// we're using specific renderers, which check themselves, if
// they need to apply Light- or DarkMode.
&& FlatStyle == FlatStyle.Standard)
{
return false;
}

return base.OwnerDraw;
}
}
#pragma warning restore WFO5001

internal override bool SupportsUiaProviders => true;

//github.com/ <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,21 @@ protected ButtonBase()

SetStyle(ControlStyles.UserMouse | ControlStyles.UserPaint, OwnerDraw);

#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.
SetStyle(ControlStyles.ApplyThemingImplicitly, true);
#pragma warning restore WFO5001

SetFlag(FlagUseMnemonic, true);
SetFlag(FlagShowToolTip, false);
}

#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.
protected override void InitializeControl(int deviceDpi)
{
// Call the base classes, and let them setup their fields.
base.InitializeControl(deviceDpi);

// Opt into DarkMode.
SetStyle(ControlStyles.ApplyThemingImplicitly, true);
}
#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.

//github.com/ <summary>
//github.com/ Gets or sets a value indicating whether the ellipsis character (...) appears at the right edge of the control,
//github.com/ denoting that the control text extends beyond the specified length of the control.
Expand Down Expand Up @@ -617,7 +624,7 @@ internal virtual Rectangle OverChangeRectangle
}
}

internal bool OwnerDraw => FlatStyle != FlatStyle.System;
private protected virtual bool OwnerDraw => FlatStyle != FlatStyle.System;

bool? ICommandBindingTargetProvider.PreviousEnabledStatus { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public CheckBox() : base()
TextAlign = ContentAlignment.MiddleLeft;
}

protected override void InitializeControl(int deviceDpi) => ScaleConstants();

private bool AccObjDoDefaultAction { get; set; }

//github.com/ <summary>
Expand Down Expand Up @@ -256,8 +258,6 @@ protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNe
ScaleConstants();
}

private protected override void InitializeConstantsForInitialDpi(int initialDpi) => ScaleConstants();

private void ScaleConstants()
{
const int LogicalFlatSystemStylePaddingWidth = 25;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,6 @@ protected override void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNe
ScaleConstants();
}

private protected override void InitializeConstantsForInitialDpi(int initialDpi)
=> ScaleConstants();

private void ScaleConstants()
{
const int LogicalFlatSystemStylePaddingWidth = 24;
Expand All @@ -244,6 +241,8 @@ private void ScaleConstants()
_flatSystemStyleMinimumHeight = LogicalToDeviceUnits(LogicalFlatSystemStyleMinimumHeight);
}

protected override void InitializeControl(int deviceDpi) => ScaleConstants();

internal override Size GetPreferredSizeCore(Size proposedConstraints)
{
if (FlatStyle != FlatStyle.System)
Expand Down
Loading








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/dotnet/winforms/pull/13551/files

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy