// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Globalization;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Windows.Forms.Automation;
using System.Windows.Forms.Layout;
using System.Windows.Forms.Primitives;
using Windows.Win32.Graphics.Dwm;
using Windows.Win32.Graphics.GdiPlus;
using Windows.Win32.System.Ole;
using Windows.Win32.UI.Accessibility;
using Windows.Win32.UI.Input.KeyboardAndMouse;
using Com = Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
using Encoding = System.Text.Encoding;

namespace System.Windows.Forms;

/// <summary>
///  Defines the base class for controls, which are components with visual representation.
/// </summary>
/// <remarks>
///  <para>
///   Do not add instance variables to Control absolutely necessary. Every control on a form has the overhead of
///   all of these variables.
///  </para>
/// </remarks>
[DefaultProperty(nameof(Text))]
[DefaultEvent(nameof(Click))]
[Designer($"System.Windows.Forms.Design.ControlDesigner, {AssemblyRef.SystemDesign}")]
[DesignerSerializer(
    $"System.Windows.Forms.Design.ControlCodeDomSerializer, {AssemblyRef.SystemDesign}",
    $"System.ComponentModel.Design.Serialization.CodeDomSerializer, {AssemblyRef.SystemDesign}")]
[ToolboxItemFilter("System.Windows.Forms")]
public unsafe partial class Control :
    Component,
    ISupportOleDropSource,
    IDropTarget,
    ISynchronizeInvoke,
    IWin32Window,
    IArrangedElement,
    IBindableComponent,
    IKeyboardToolTip,
    IHandle<HWND>
{
#if DEBUG
    private static readonly BooleanSwitch s_bufferPinkRect = new(
        "BufferPinkRect",
        "Renders a pink rectangle with painting double buffered controls");
    private static readonly BooleanSwitch s_bufferDisabled = new(
        "BufferDisabled",
        "Makes double buffered controls non-double buffered");
#endif

    // Feature switch, when set to false, design time features of controls are not supported in trimmed applications.
    [FeatureSwitchDefinition("System.Windows.Forms.Control.AreDesignTimeFeaturesSupported")]
#pragma warning disable IDE0075 // Simplify conditional expression - the simpler expression is hard to read
    internal static bool AreDesignTimeFeaturesSupported { get; } =
        AppContext.TryGetSwitch("System.Windows.Forms.Control.AreDesignTimeFeaturesSupported", out bool isEnabled)
            ? isEnabled
            : true;

    // Feature switch, when set to true, used for trimming to access ComponentModel in a trim safe manner
    [FeatureSwitchDefinition("System.Windows.Forms.Control.UseComponentModelRegisteredTypes")]
    internal static bool UseComponentModelRegisteredTypes { get; } =
        AppContext.TryGetSwitch("System.Windows.Forms.Control.UseComponentModelRegisteredTypes", out bool isEnabled)
            ? isEnabled
            : false;
#pragma warning restore IDE0075

    private static readonly uint WM_GETCONTROLNAME = PInvoke.RegisterWindowMessage("WM_GETCONTROLNAME");
    private static readonly uint WM_GETCONTROLTYPE = PInvoke.RegisterWindowMessage("WM_GETCONTROLTYPE");

    private static readonly object s_autoSizeChangedEvent = new();
    private static readonly object s_keyDownEvent = new();
    private static readonly object s_keyPressEvent = new();
    private static readonly object s_keyUpEvent = new();
    private static readonly object s_mouseDownEvent = new();
    private static readonly object s_mouseEnterEvent = new();
    private static readonly object s_mouseLeaveEvent = new();
    private static readonly object s_dpiChangedBeforeParentEvent = new();
    private static readonly object s_dpiChangedAfterParentEvent = new();
    private static readonly object s_mouseHoverEvent = new();
    private static readonly object s_mouseMoveEvent = new();
    private static readonly object s_mouseUpEvent = new();
    private static readonly object s_mouseWheelEvent = new();
    private static readonly object s_clickEvent = new();
    private static readonly object s_clientSizeEvent = new();
    private static readonly object s_doubleClickEvent = new();
    private static readonly object s_mouseClickEvent = new();
    private static readonly object s_mouseDoubleClickEvent = new();
    private static readonly object s_mouseCaptureChangedEvent = new();
    private static readonly object s_moveEvent = new();
    private static readonly object s_resizeEvent = new();
    private static readonly object s_layoutEvent = new();
    private static readonly object s_gotFocusEvent = new();
    private static readonly object s_lostFocusEvent = new();
    private static readonly object s_enterEvent = new();
    private static readonly object s_leaveEvent = new();
    private static readonly object s_handleCreatedEvent = new();
    private static readonly object s_handleDestroyedEvent = new();
    private static readonly object s_controlAddedEvent = new();
    private static readonly object s_controlRemovedEvent = new();
    private static readonly object s_changeUICuesEvent = new();
    private static readonly object s_systemColorsChangedEvent = new();
    private static readonly object s_validatingEvent = new();
    private static readonly object s_validatedEvent = new();
    private static readonly object s_styleChangedEvent = new();
    private static readonly object s_imeModeChangedEvent = new();
    private static readonly object s_helpRequestedEvent = new();
    private static readonly object s_paintEvent = new();
    private static readonly object s_invalidatedEvent = new();
    private static readonly object s_queryContinueDragEvent = new();
    private static readonly object s_giveFeedbackEvent = new();
    private static readonly object s_dragEnterEvent = new();
    private static readonly object s_dragLeaveEvent = new();
    private static readonly object s_dragOverEvent = new();
    private static readonly object s_dragDropEvent = new();
    private static readonly object s_queryAccessibilityHelpEvent = new();
    private static readonly object s_backgroundImageEvent = new();
    private static readonly object s_backgroundImageLayoutEvent = new();
    private static readonly object s_bindingContextEvent = new();
    private static readonly object s_backColorEvent = new();
    private static readonly object s_parentEvent = new();
    private static readonly object s_visibleEvent = new();
    private static readonly object s_textEvent = new();
    private static readonly object s_tabStopEvent = new();
    private static readonly object s_tabIndexEvent = new();
    private static readonly object s_sizeEvent = new();
    private static readonly object s_rightToLeftEvent = new();
    private static readonly object s_locationEvent = new();
    private static readonly object s_foreColorEvent = new();
    private static readonly object s_fontEvent = new();
    private static readonly object s_enabledEvent = new();
    private static readonly object s_dockEvent = new();
    private static readonly object s_cursorEvent = new();
    private static readonly object s_contextMenuStripEvent = new();
    private static readonly object s_causesValidationEvent = new();
    private static readonly object s_regionChangedEvent = new();
    private static readonly object s_marginChangedEvent = new();
    // This needs to be internal for derived controls to access it.
    private protected static readonly object s_paddingChangedEvent = new();
    private static readonly object s_previewKeyDownEvent = new();
    private static readonly object s_dataContextEvent = new();

    private static MessageId s_threadCallbackMessage;
    private static ContextCallback? s_invokeMarshaledCallbackHelperDelegate;

    [ThreadStatic]
    private static bool t_inCrossThreadSafeCall;

    [ThreadStatic]
    internal static HelpInfo? t_currentHelpInfo;

    private static FontHandleWrapper? s_defaultFontHandleWrapper;

    internal const string DarkModeIdentifier = "DarkMode";
    internal const string ExplorerThemeIdentifier = "Explorer";
    internal const string ItemsViewThemeIdentifier = "ItemsView";
    internal const string ComboBoxButtonThemeIdentifier = "CFD";

    private const short PaintLayerBackground = 1;
    private const short PaintLayerForeground = 2;

    private const byte RequiredScalingEnabledMask = 0x10;
    private const byte RequiredScalingMask = 0x0F;

    private const byte HighOrderBitMask = 0x80;

    private static Font? s_defaultFont;

    internal ControlCollection? ChildControls { get; private set; }

    // Property store keys for properties. The property store allocates most efficiently
    // in groups of four, so we try to lump properties in groups of four based on how
    // likely they are going to be used in a group.
    private static readonly int s_namePropertyProperty = PropertyStore.CreateKey();
    private static readonly int s_backBrushProperty = PropertyStore.CreateKey();
    private static readonly int s_fontHeightProperty = PropertyStore.CreateKey();
    private static readonly int s_currentAmbientFontProperty = PropertyStore.CreateKey();

    private static readonly int s_backColorProperty = PropertyStore.CreateKey();
    private static readonly int s_foreColorProperty = PropertyStore.CreateKey();
    private static readonly int s_fontProperty = PropertyStore.CreateKey();

    private static readonly int s_backgroundImageProperty = PropertyStore.CreateKey();
    private static readonly int s_fontHandleWrapperProperty = PropertyStore.CreateKey();
    private static readonly int s_userDataProperty = PropertyStore.CreateKey();

    private static readonly int s_cursorProperty = PropertyStore.CreateKey();
    private static readonly int s_regionProperty = PropertyStore.CreateKey();
    private static readonly int s_rightToLeftProperty = PropertyStore.CreateKey();

    private static readonly int s_bindingsProperty = PropertyStore.CreateKey();
    private static readonly int s_bindingManagerProperty = PropertyStore.CreateKey();
    private static readonly int s_accessibleDefaultActionProperty = PropertyStore.CreateKey();
    private static readonly int s_accessibleDescriptionProperty = PropertyStore.CreateKey();

    private static readonly int s_accessibilityProperty = PropertyStore.CreateKey();
    private static readonly int s_ncAccessibilityProperty = PropertyStore.CreateKey();
    private static readonly int s_accessibleNameProperty = PropertyStore.CreateKey();
    private static readonly int s_accessibleRoleProperty = PropertyStore.CreateKey();

    private static readonly int s_activeXImplProperty = PropertyStore.CreateKey();
    private static readonly int s_controlVersionInfoProperty = PropertyStore.CreateKey();
    private static readonly int s_backgroundImageLayoutProperty = PropertyStore.CreateKey();

    private static readonly int s_contextMenuStripProperty = PropertyStore.CreateKey();
    private static readonly int s_autoScrollOffsetProperty = PropertyStore.CreateKey();
    private static readonly int s_useCompatibleTextRenderingProperty = PropertyStore.CreateKey();

    private static readonly int s_imeWmCharsToIgnoreProperty = PropertyStore.CreateKey();
    private static readonly int s_imeModeProperty = PropertyStore.CreateKey();
    private static readonly int s_disableImeModeChangedCountProperty = PropertyStore.CreateKey();
    private static readonly int s_lastCanEnableImeProperty = PropertyStore.CreateKey();

    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 bool s_needToLoadComCtl = true;

    // This switch determines the default text rendering engine to use by some controls that support switching rendering engine.
    // CheckedListBox, PropertyGrid, GroupBox, Label and LinkLabel, and ButtonBase controls.
    // True means use GDI+, false means use GDI (TextRenderer).
    internal static bool UseCompatibleTextRenderingDefault { get; set; } = true;

    // Control instance members
    //
    // Note: Do not add anything to this list unless absolutely necessary.
    //       Every control on a form has the overhead of all of these
    //       variables!

    // Resist the temptation to make this variable 'internal' rather than
    // private. Handle access should be tightly controlled, and is in this
    // file. Making it 'internal' makes controlling it quite difficult.
    private readonly ControlNativeWindow _window;

    private Control? _parent;
    private WeakReference<Control>? _reflectParent;
    private CreateParams? _createParams;
    private int _x;
    private int _y;
    private int _width;
    private int _height;
    private int _clientWidth;
    private int _clientHeight;
    private States _state;
    private ExtendedStates _extendedState;

    /// <summary>
    ///  User supplied control style
    /// </summary>
    private ControlStyles _controlStyle;
    private int _tabIndex;
    // See ControlStyles.CacheText for usage notes
    private string? _text;
    // bits 0-4: BoundsSpecified stored in RequiredScaling property. Bit 5: RequiredScalingEnabled property.
    private byte _requiredScaling;
    private TRACKMOUSEEVENT _trackMouseEvent;
    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
    private UICuesStates _uiCuesState;

    // Stores scaled font from Dpi changed values. This is required to distinguish the Font change from
    // Dpi changed events and explicit Font change/assignment.
    private Font? _scaledControlFont;
    private FontHandleWrapper? _scaledFontWrapper;

    // ContainerControls like 'PropertyGrid' scale their children when they resize.
    // no explicit scaling of children required in such cases. They have specific logic.
    internal bool _doNotScaleChildren;

    // Contains a collection of calculated fonts for various Dpi values of the control in the PerMonV2 mode.
    private Dictionary<int, Font>? _dpiFonts;

    // Flag to signify whether any child controls necessitate the calculation of AnchorsInfo,
    // particularly in cases involving nested containers.
    internal bool _childControlsNeedAnchorLayout;

    // Inform whether the AnchorsInfo needs to be reevaluated,
    // especially when the control's bounds have been altered explicitly.
    internal bool _forceAnchorCalculations;

    internal byte LayoutSuspendCount { get; private set; }

    /// <summary>
    ///  Initializes a new instance of the <see cref="Control"/> class.
    /// </summary>
    public Control() : this(true)
    {
    }

    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;
        _window = new ControlNativeWindow(this);
        RequiredScalingEnabled = true;
        RequiredScaling = BoundsSpecified.All;
        _tabIndex = -1;

        _state = States.Visible | States.Enabled | States.TabStop | States.CausesValidation;
        _extendedState = ExtendedStates.InterestedInUserPreferenceChanged;

        SetStyle(
            ControlStyles.AllPaintingInWmPaint
                | ControlStyles.UserPaint
                | ControlStyles.StandardClick
                | ControlStyles.StandardDoubleClick
                | ControlStyles.UseTextForAccessibility
                | ControlStyles.Selectable,
            true);

        // 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;
        }

        if (DefaultMinimumSize != CommonProperties.DefaultMinimumSize)
        {
            MinimumSize = DefaultMinimumSize;
        }

        if (DefaultMaximumSize != CommonProperties.DefaultMaximumSize)
        {
            MaximumSize = DefaultMaximumSize;
        }

        // Compute our default size.
        Size defaultSize = DefaultSize;
        _width = defaultSize.Width;
        _height = defaultSize.Height;

        // DefaultSize may have hit GetPreferredSize causing a PreferredSize to be cached. The
        // PreferredSize may change as a result of the current size. Since a  SetBoundsCore did
        // not happen, so we need to clear the preferredSize cache manually.
        CommonProperties.xClearPreferredSizeCache(this);

        if (_width != 0 && _height != 0)
        {
            RECT rect = default;

            CreateParams cp = CreateParams;

            AdjustWindowRectExForControlDpi(ref rect, (WINDOW_STYLE)cp.Style, false, (WINDOW_EX_STYLE)cp.ExStyle);
            _clientWidth = _width - rect.Width;
            _clientHeight = _height - rect.Height;
        }

        // Set up for async operations on this thread.
        if (autoInstallSyncContext)
        {
            WindowsFormsSynchronizationContext.InstallIfNeeded();
        }
    }

    /// <summary>
    ///  Initializes a new instance of the <see cref="Control"/> class.
    /// </summary>
    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)
    {
    }

    /// <summary>
    ///  Initializes a new instance of the <see cref="Control"/> class.
    /// </summary>
    public Control(Control? parent, string? text) : this()
    {
        Parent = parent;
        Text = text;
    }

    /// <summary>
    ///  Initializes a new instance of the <see cref="Control"/> class.
    /// </summary>
    public Control(Control? parent, string? text, int left, int top, int width, int height) : this(parent, text)
    {
        Location = new Point(left, top);
        Size = new Size(width, height);
    }

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

    /// <summary>
    ///  The Accessibility Object for this Control
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlAccessibilityObjectDescr))]
    public AccessibleObject AccessibilityObject
    {
        get
        {
            if (!Properties.TryGetValue(s_accessibilityProperty, out AccessibleObject? accessibleObject))
            {
                accessibleObject = CreateAccessibilityInstance().OrThrowIfNull();
                Properties.AddValue(s_accessibilityProperty, accessibleObject);
            }

            Debug.Assert(accessibleObject is not null, "Failed to create accessibility object");
            return accessibleObject;
        }
    }

    /// <summary>
    ///  Private accessibility object for control, used to wrap the object that
    ///  OLEACC.DLL creates to represent the control's non-client (NC) region.
    /// </summary>
    private AccessibleObject NcAccessibilityObject
    {
        get
        {
            if (!Properties.TryGetValue(s_ncAccessibilityProperty, out AccessibleObject? ncAccessibleObject))
            {
                ncAccessibleObject = new ControlAccessibleObject(this, (int)OBJECT_IDENTIFIER.OBJID_WINDOW);
                Properties.AddValue(s_ncAccessibilityProperty, ncAccessibleObject);
            }

            Debug.Assert(ncAccessibleObject is not null, "Failed to create NON-CLIENT accessibility object");
            return ncAccessibleObject;
        }
    }

    /// <summary>
    ///  Returns a specific AccessibleObject associated w/ the objectID
    /// </summary>
    protected virtual AccessibleObject? GetAccessibilityObjectById(int objectId) =>
        this is IAutomationLiveRegion ? AccessibilityObject : null;

    /// <summary>
    ///  The default action description of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAccessibility))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlAccessibleDefaultActionDescr))]
    public string? AccessibleDefaultActionDescription
    {
        get => Properties.GetValueOrDefault<string>(s_accessibleDefaultActionProperty);
        set => Properties.AddOrRemoveValue(s_accessibleDefaultActionProperty, value);
    }

    /// <summary>
    ///  The accessible description of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAccessibility))]
    [DefaultValue(null)]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlAccessibleDescriptionDescr))]
    public string? AccessibleDescription
    {
        get => Properties.GetValueOrDefault<string>(s_accessibleDescriptionProperty);
        set => Properties.AddOrRemoveValue(s_accessibleDescriptionProperty, value);
    }

    /// <summary>
    ///  The accessible name of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAccessibility))]
    [DefaultValue(null)]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlAccessibleNameDescr))]
    public string? AccessibleName
    {
        get => Properties.GetValueOrDefault<string>(s_accessibleNameProperty);
        set => Properties.AddOrRemoveValue(s_accessibleNameProperty, value);
    }

    /// <summary>
    ///  The accessible role of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAccessibility))]
    [DefaultValue(AccessibleRole.Default)]
    [SRDescription(nameof(SR.ControlAccessibleRoleDescr))]
    public AccessibleRole AccessibleRole
    {
        get => Properties.GetValueOrDefault(s_accessibleRoleProperty, AccessibleRole.Default);
        set
        {
            SourceGenerated.EnumValidator.Validate(value);
            Properties.AddOrRemoveValue(s_accessibleRoleProperty, value, defaultValue: AccessibleRole.Default);
        }
    }

    /// <summary>
    ///  The AllowDrop property. If AllowDrop is set to <see langword="true"/> then
    ///  this control will allow drag and drop operations and events to be used.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [DefaultValue(false)]
    [SRDescription(nameof(SR.ControlAllowDropDescr))]
    public virtual bool AllowDrop
    {
        get => GetState(States.AllowDrop);
        set
        {
            if (GetState(States.AllowDrop) == value)
            {
                return;
            }

            SetState(States.AllowDrop, value);

            if (!IsHandleCreated)
            {
                return;
            }

            try
            {
                SetAcceptDrops(value);
            }
            catch
            {
                // If there is an error, back out the AllowDrop state.
                SetState(States.AllowDrop, !value);
                throw;
            }
        }
    }

    // Queries the Site for AmbientProperties. May return null.
    // Do not confuse with inheritedProperties -- the service is turned to
    // after we've exhausted inheritedProperties.
    private AmbientProperties? AmbientPropertiesService
    {
        get
        {
            if (Properties.TryGetValueOrNull(s_ambientPropertiesServiceProperty, out AmbientProperties? ambientProperties))
            {
                return ambientProperties;
            }

            ambientProperties = Site is not null ? Site.GetService<AmbientProperties>() : GetService<AmbientProperties>();

            if (ambientProperties is not null)
            {
                Properties.AddValue(s_ambientPropertiesServiceProperty, ambientProperties);
            }

            return ambientProperties;
        }
    }

    /// <summary>
    ///  The current value of the anchor property. The anchor property determines which edges of the control are
    ///  anchored to the container's edges.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    [DefaultValue(CommonProperties.DefaultAnchor)]
    [SRDescription(nameof(SR.ControlAnchorDescr))]
    [RefreshProperties(RefreshProperties.Repaint)]
    public virtual AnchorStyles Anchor
    {
        get => DefaultLayout.GetAnchor(this);
        set => DefaultLayout.SetAnchor(this, value);
    }

    [SRCategory(nameof(SR.CatLayout))]
    [RefreshProperties(RefreshProperties.All)]
    [Localizable(true)]
    [DefaultValue(CommonProperties.DefaultAutoSize)]
    [SRDescription(nameof(SR.ControlAutoSizeDescr))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual bool AutoSize
    {
        get => CommonProperties.GetAutoSize(this);
        set
        {
            if (value == AutoSize)
            {
                return;
            }

            CommonProperties.SetAutoSize(this, value);
            if (ParentInternal is { } parent)
            {
                // DefaultLayout does not keep anchor information until it needs to. When
                // AutoSize became a common property, we could no longer blindly call into
                // DefaultLayout, so now we do a special InitLayout just for DefaultLayout.
                if (value && parent.LayoutEngine == DefaultLayout.Instance)
                {
                    parent.LayoutEngine.InitLayout(this, BoundsSpecified.Size);
                }

                LayoutTransaction.DoLayout(parent, this, PropertyNames.AutoSize);
            }

            OnAutoSizeChanged(EventArgs.Empty);
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnAutoSizeChangedDescr))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public event EventHandler? AutoSizeChanged
    {
        add => Events.AddHandler(s_autoSizeChangedEvent, value);
        remove => Events.RemoveHandler(s_autoSizeChangedEvent, value);
    }

    /// <summary>
    ///  Controls the location of where this control is scrolled to in ScrollableControl.ScrollControlIntoView.
    ///  Default is the upper left hand corner of the control.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DefaultValue(typeof(Point), "0, 0")]
    public virtual Point AutoScrollOffset
    {
        get => Properties.GetValueOrDefault(s_autoScrollOffsetProperty, Point.Empty);
        set => Properties.AddOrRemoveValue(s_autoScrollOffsetProperty, value, defaultValue: Point.Empty);
    }

    protected void SetAutoSizeMode(AutoSizeMode mode) => CommonProperties.SetAutoSizeMode(this, mode);

    protected AutoSizeMode GetAutoSizeMode() => CommonProperties.GetAutoSizeMode(this);

    // Public because this is interesting for ControlDesigners.
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public virtual LayoutEngine LayoutEngine => DefaultLayout.Instance;

    /// <summary>
    ///  The GDI brush for our background color.
    ///  Whidbey Note: Made this internal, since we need to use this in ButtonStandardAdapter. Also, renamed
    ///         from BackBrush to BackColorBrush due to a naming conflict with DataGrid's BackBrush.
    /// </summary>
    internal HBRUSH BackColorBrush
    {
        get
        {
            if (Properties.TryGetValue(s_backBrushProperty, out HBRUSH customBackBrush))
            {
                // We already have a valid brush. Unbox, and return.
                return customBackBrush;
            }

            if (!Properties.ContainsKey(s_backColorProperty))
            {
                // No custom back color. See if we can get to our parent.
                // The color check here is to account for parents and children who
                // override the BackColor property.
                if (ParentInternal is { } parent && parent.BackColor == BackColor)
                {
                    return parent.BackColorBrush;
                }
            }

            // No parent, or we have a custom back color. Either way, we need to
            // create our own.
            Color color = BackColor;
            HBRUSH backBrush;

            if (color.IsSystemColor)
            {
                backBrush = PInvokeCore.GetSysColorBrush(color);
                SetState(States.OwnCtlBrush, false);
            }
            else
            {
                backBrush = PInvokeCore.CreateSolidBrush((COLORREF)(uint)ColorTranslator.ToWin32(color));
                SetState(States.OwnCtlBrush, true);
            }

            Debug.Assert(!backBrush.IsNull, "Failed to create brushHandle");
            Properties.AddValue(s_backBrushProperty, backBrush);

            return backBrush;
        }
    }

    /// <summary>
    ///  Gets or sets the data context for the purpose of data binding.
    ///  This is an ambient property.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   Data context is a concept that allows elements to inherit information from their parent elements
    ///   about the data source that is used for binding. It's the duty of deriving controls which inherit from
    ///   this class to handle the provided data source accordingly. For example, UserControls, which use
    ///   <see cref="BindingSource"/> components for data binding scenarios could either handle the
    ///   <see cref="DataContextChanged"/> event or override <see cref="OnDataContextChanged(EventArgs)"/> to provide
    ///   the relevant data from the data context to a BindingSource component's <see cref="BindingSource.DataSource"/>.
    ///  </para>
    /// </remarks>
    [SRCategory(nameof(SR.CatData))]
    [Browsable(false)]
    [Bindable(true)]
    public virtual object? DataContext
    {
        get => Properties.TryGetValue(s_dataContextProperty, out object? value)
            ? value
            : ParentInternal?.DataContext;
        set
        {
            if (Equals(value, DataContext))
            {
                return;
            }

            // When DataContext was different than its parent before, but now it is about to become the same,
            // we're removing it altogether, so it can inherit the value from its parent.
            if (Properties.ContainsKey(s_dataContextProperty) && Equals(ParentInternal?.DataContext, value))
            {
                Properties.RemoveValue(s_dataContextProperty);
                OnDataContextChanged(EventArgs.Empty);
                return;
            }

            Properties.AddValue(s_dataContextProperty, value);
            OnDataContextChanged(EventArgs.Empty);
        }
    }

    private bool ShouldSerializeDataContext()
        => Properties.ContainsKey(s_dataContextProperty);

    private void ResetDataContext()
        => Properties.RemoveValue(s_dataContextProperty);

    /// <summary>
    ///  The background color of this control. This is an ambient property and
    ///  will always return a non-null value.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [DispId(PInvokeCore.DISPID_BACKCOLOR)]
    [SRDescription(nameof(SR.ControlBackColorDescr))]
    public virtual Color BackColor
    {
        get
        {
            Color c = RawBackColor; // inheritedProperties.BackColor
            if (!c.IsEmpty)
            {
                return c;
            }

            Control? parent = ParentInternal;
            if (parent is not null && parent.CanAccessProperties)
            {
                c = parent.BackColor;
                if (IsValidBackColor(c))
                {
                    return c;
                }
            }

            if (IsActiveX)
            {
                c = ActiveXAmbientBackColor;
            }

            if (c.IsEmpty)
            {
                AmbientProperties? ambient = AmbientPropertiesService;
                if (ambient is not null)
                {
                    c = ambient.BackColor;
                }
            }

            return !c.IsEmpty && IsValidBackColor(c) ? c : DefaultBackColor;
        }
        set
        {
            if (!value.Equals(Color.Empty) && !GetStyle(ControlStyles.SupportsTransparentBackColor) && value.A < 255)
            {
                throw new ArgumentException(SR.TransparentBackColorNotAllowed);
            }

            Color previous = BackColor;
            Properties.AddOrRemoveValue(s_backColorProperty, value);
            if (!previous.Equals(BackColor))
            {
                OnBackColorChanged(EventArgs.Empty);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnBackColorChangedDescr))]
    public event EventHandler? BackColorChanged
    {
        add => Events.AddHandler(s_backColorEvent, value);
        remove => Events.RemoveHandler(s_backColorEvent, value);
    }

    /// <summary>
    ///  The background image of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [DefaultValue(null)]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlBackgroundImageDescr))]
    public virtual Image? BackgroundImage
    {
        get => Properties.GetValueOrDefault<Image>(s_backgroundImageProperty);
        set
        {
            if (BackgroundImage == value)
            {
                return;
            }

            Properties.AddOrRemoveValue(s_backgroundImageProperty, value);
            OnBackgroundImageChanged(EventArgs.Empty);
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnBackgroundImageChangedDescr))]
    public event EventHandler? BackgroundImageChanged
    {
        add => Events.AddHandler(s_backgroundImageEvent, value);
        remove => Events.RemoveHandler(s_backgroundImageEvent, value);
    }

    /// <summary>
    ///  The BackgroundImageLayout of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [DefaultValue(ImageLayout.Tile)]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlBackgroundImageLayoutDescr))]
    public virtual ImageLayout BackgroundImageLayout
    {
        get => Properties.GetValueOrDefault(s_backgroundImageLayoutProperty, ImageLayout.Tile);
        set
        {
            if (BackgroundImageLayout == value)
            {
                return;
            }

            SourceGenerated.EnumValidator.Validate(value);

            // Check if the value is either center, stretch or zoom;
            if (value is ImageLayout.Center or ImageLayout.Zoom or ImageLayout.Stretch)
            {
                SetStyle(ControlStyles.ResizeRedraw, true);

                // Only for images that support transparency.
                if (ControlPaint.IsImageTransparent(BackgroundImage))
                {
                    DoubleBuffered = true;
                }
            }

            Properties.AddOrRemoveValue(s_backgroundImageLayoutProperty, value, defaultValue: ImageLayout.Tile);
            OnBackgroundImageLayoutChanged(EventArgs.Empty);
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnBackgroundImageLayoutChangedDescr))]
    public event EventHandler? BackgroundImageLayoutChanged
    {
        add => Events.AddHandler(s_backgroundImageLayoutEvent, value);
        remove => Events.RemoveHandler(s_backgroundImageLayoutEvent, value);
    }

    // Set/reset by ContainerControl.AssignActiveControlInternal
    internal bool BecomingActiveControl
    {
        get => GetExtendedState(ExtendedStates.BecomingActiveControl);
        set
        {
            if (value != BecomingActiveControl)
            {
                Application.ThreadContext.FromCurrent().ActivatingControl = value ? this : null;
                SetExtendedState(ExtendedStates.BecomingActiveControl, value);
            }
        }
    }

    private bool ShouldSerializeAccessibleName() => !string.IsNullOrEmpty(AccessibleName);

    [EditorBrowsable(EditorBrowsableState.Never)]
    public void ResetBindings()
    {
        if (!Binding.IsSupported)
        {
            // This gets called with Dispose that needs to be handled, a throwing is not appropriate in this case.
            return;
        }

        Properties.GetValueOrDefault<ControlBindingsCollection>(s_bindingsProperty)?.Clear();
    }

    /// <summary>
    ///  BindingContextInternal provides a mechanism so that controls like SplitContainer that inherit from the
    ///  ContainerControl can bypass the "containerControls" bindingContext property and do what the other simple
    ///  controls do.
    /// </summary>
    internal BindingContext? BindingContextInternal
    {
        get
        {
            // See if we have locally overridden the binding manager.
            if (Properties.TryGetValue(s_bindingManagerProperty, out BindingContext? context))
            {
                return context;
            }

            // Otherwise, see if the parent has one for us.
            Control? parent = ParentInternal;
            if (parent is not null && parent.CanAccessProperties)
            {
                return parent.BindingContext;
            }

            // Otherwise, we have no binding manager available.
            return null;
        }
        set
        {
            BindingContext? oldContext = Properties.AddOrRemoveValue(s_bindingManagerProperty, value);

            if (oldContext != value)
            {
                // The property change will wire up the bindings.
                OnBindingContextChanged(EventArgs.Empty);
            }
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlBindingContextDescr))]
    public virtual BindingContext? BindingContext
    {
        get => BindingContextInternal;
        set => BindingContextInternal = value;
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnBindingContextChangedDescr))]
    public event EventHandler? BindingContextChanged
    {
        add => Events.AddHandler(s_bindingContextEvent, value);
        remove => Events.RemoveHandler(s_bindingContextEvent, value);
    }

    /// <summary>
    ///  The bottom coordinate of this control.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlBottomDescr))]
    [SRCategory(nameof(SR.CatLayout))]
    public int Bottom => _y + _height;

    /// <summary>
    ///  The bounds of this control. This is the window coordinates of the
    ///  control in parent client coordinates.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlBoundsDescr))]
    [SRCategory(nameof(SR.CatLayout))]
    public Rectangle Bounds
    {
        get => new(_x, _y, _width, _height);
        set => SetBounds(value.X, value.Y, value.Width, value.Height, BoundsSpecified.All);
    }

    internal virtual bool CanAccessProperties => true;

    /// <summary>
    ///  Indicates whether the control can receive focus. This
    ///  property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlCanFocusDescr))]
    public bool CanFocus => IsHandleCreated && PInvoke.IsWindowVisible(this) && PInvoke.IsWindowEnabled(this);

    /// <summary>
    ///  Determines if events can be fired on the control. If this control is being
    ///  hosted as an ActiveX control, this property will return false if the ActiveX
    ///  control has its events frozen.
    /// </summary>
    protected override bool CanRaiseEvents => !IsActiveX || !ActiveXEventsFrozen;

    /// <summary>
    ///  Indicates whether the control can be selected. This property
    ///  is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlCanSelectDescr))]
    public bool CanSelect => CanSelectCore();

    /// <summary>
    ///  Indicates whether the control has captured the mouse.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlCaptureDescr))]
    public bool Capture
    {
        get => IsHandleCreated && PInvoke.GetCapture() == HWND;
        set
        {
            if (Capture == value)
            {
                return;
            }

            if (value)
            {
                PInvoke.SetCapture(this);
            }
            else
            {
                PInvoke.ReleaseCapture();
            }
        }
    }

    /// <summary>
    ///  Indicates whether entering the control causes validation on the controls requiring validation.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [DefaultValue(true)]
    [SRDescription(nameof(SR.ControlCausesValidationDescr))]
    public bool CausesValidation
    {
        get => GetState(States.CausesValidation);
        set
        {
            if (value != CausesValidation)
            {
                SetState(States.CausesValidation, value);
                OnCausesValidationChanged(EventArgs.Empty);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnCausesValidationChangedDescr))]
    public event EventHandler? CausesValidationChanged
    {
        add => Events.AddHandler(s_causesValidationEvent, value);
        remove => Events.RemoveHandler(s_causesValidationEvent, value);
    }

    /// <summary>
    ///  This is for perf. Turn this property on to temporarily enable text caching. This is good for operations such
    ///  as layout or painting where we don't expect the text to change (we will update the cache if it does). It
    ///  prevents us from sending a ton of messages during layout. See the <see cref="PaintWithErrorHandling(PaintEventArgs, short)"/>
    ///  function.
    /// </summary>
    internal bool CacheTextInternal
    {
        get
        {
            // Check if we're caching text.
            int cacheTextCounter = Properties.GetValueOrDefault<int>(s_cacheTextCountProperty);

            return cacheTextCounter > 0 || GetStyle(ControlStyles.CacheText);
        }
        set
        {
            // If this control always caches text or the handle hasn't been created, just bail.
            if (GetStyle(ControlStyles.CacheText) || !IsHandleCreated)
            {
                return;
            }

            // Otherwise, get the state and update the cache if necessary.
            int cacheTextCounter = Properties.GetValueOrDefault<int>(s_cacheTextCountProperty);

            if (value)
            {
                if (cacheTextCounter == 0)
                {
                    Properties.AddOrRemoveValue(s_cacheTextFieldProperty, _text);
                    _text ??= WindowText;
                }

                cacheTextCounter++;
            }
            else
            {
                cacheTextCounter--;
                if (cacheTextCounter == 0)
                {
                    _text = Properties.GetValueOrDefault<string?>(s_cacheTextFieldProperty);
                }
            }

            Properties.AddOrRemoveValue(s_cacheTextCountProperty, cacheTextCounter);
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlCheckForIllegalCrossThreadCalls))]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public static bool CheckForIllegalCrossThreadCalls { get; set; } = Debugger.IsAttached;

    /// <summary>
    ///  The client rect of the control.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlClientRectangleDescr))]
    public Rectangle ClientRectangle => new Rectangle(0, 0, _clientWidth, _clientHeight);

    /// <summary>
    ///  The size of the clientRect.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlClientSizeDescr))]
    public Size ClientSize
    {
        get => new(_clientWidth, _clientHeight);
        set => SetClientSizeCore(value.Width, value.Height);
    }

    /// <summary>
    ///  Fired when ClientSize changes.
    /// </summary>
    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnClientSizeChangedDescr))]
    public event EventHandler? ClientSizeChanged
    {
        add => Events.AddHandler(s_clientSizeEvent, value);
        remove => Events.RemoveHandler(s_clientSizeEvent, value);
    }

    /// <summary>
    ///  Retrieves the company name of this specific component.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlCompanyNameDescr))]
    public string CompanyName => VersionInfo.CompanyName;

    /// <summary>
    ///  Indicates whether the control or one of its children currently has the system
    ///  focus. This property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlContainsFocusDescr))]
    public bool ContainsFocus
    {
        get
        {
            if (!IsHandleCreated)
            {
                return false;
            }

            HWND focusHwnd = PInvoke.GetFocus();
            return !focusHwnd.IsNull && (focusHwnd == Handle || PInvoke.IsChild(this, focusHwnd));
        }
    }

    /// <summary>
    ///  The contextMenuStrip associated with this control. The contextMenuStrip
    ///  will be shown when the user right clicks the mouse on the control.
    ///  Note: if a context menu is also assigned, it will take precedence over this property.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [DefaultValue(null)]
    [SRDescription(nameof(SR.ControlContextMenuDescr))]
    public virtual ContextMenuStrip? ContextMenuStrip
    {
        get => Properties.GetValueOrDefault<ContextMenuStrip>(s_contextMenuStripProperty);
        set
        {
            ContextMenuStrip? oldValue = Properties.AddOrRemoveValue(s_contextMenuStripProperty, value);

            if (oldValue == value)
            {
                return;
            }

            if (oldValue is not null)
            {
                oldValue.Disposed -= DetachContextMenuStrip;
            }

            if (value is not null)
            {
                value.Disposed += DetachContextMenuStrip;
            }

            OnContextMenuStripChanged(EventArgs.Empty);
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlContextMenuStripChangedDescr))]
    public event EventHandler? ContextMenuStripChanged
    {
        add => Events.AddHandler(s_contextMenuStripEvent, value);
        remove => Events.RemoveHandler(s_contextMenuStripEvent, value);
    }

    /// <summary>
    ///  Collection of child controls.
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [SRDescription(nameof(SR.ControlControlsDescr))]
    public ControlCollection Controls => ChildControls ??= CreateControlsInstance();

    /// <summary>
    ///  Indicates whether the control has been created. This property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlCreatedDescr))]
    public bool Created => GetState(States.Created);

    /// <summary>
    ///  Returns the CreateParams used to create the handle for this control.
    ///  Inheriting classes should call base.CreateParams in the manor
    ///  below:
    /// </summary>
    protected virtual CreateParams CreateParams
    {
        get
        {
            // CLR4.0 or later, comctl32.dll needs to be loaded explicitly.
            if (s_needToLoadComCtl)
            {
                if ((PInvoke.GetModuleHandle(Libraries.Comctl32) != 0)
                    || (PInvoke.LoadComctl32(Application.StartupPath) != 0))
                {
                    s_needToLoadComCtl = false;
                }
                else
                {
                    int lastWin32Error = Marshal.GetLastWin32Error();
                    throw new Win32Exception(lastWin32Error, string.Format(SR.LoadDLLError, Libraries.Comctl32));
                }
            }

            // In a typical control this is accessed ten times to create and show a control.
            // It is a net memory savings, then, to maintain a copy on control.
            _createParams ??= new CreateParams();

            CreateParams cp = _createParams;
            cp.Style = 0;
            cp.ExStyle = 0;
            cp.ClassStyle = 0;
            cp.Caption = _text;

            cp.X = _x;
            cp.Y = _y;
            cp.Width = _width;
            cp.Height = _height;

            cp.Style = (int)WINDOW_STYLE.WS_CLIPCHILDREN;
            if (GetStyle(ControlStyles.ContainerControl))
            {
                cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_CONTROLPARENT;
            }

            cp.ClassStyle = (int)WNDCLASS_STYLES.CS_DBLCLKS;

            if (!_state.HasFlag(States.TopLevel))
            {
                // When the window is actually created, we will parent WS_CHILD windows to the
                // parking form if cp.parent == 0.
                cp.Parent = _parent is null ? IntPtr.Zero : _parent.InternalHandle;
                cp.Style |= (int)(WINDOW_STYLE.WS_CHILD | WINDOW_STYLE.WS_CLIPSIBLINGS);
            }
            else
            {
                cp.Parent = IntPtr.Zero;
            }

            if (_state.HasFlag(States.TabStop))
            {
                cp.Style |= (int)WINDOW_STYLE.WS_TABSTOP;
            }

            if (_state.HasFlag(States.Visible))
            {
                cp.Style |= (int)WINDOW_STYLE.WS_VISIBLE;
            }

            // Unlike Visible, Windows doesn't correctly inherit disabled state from its parent -- an enabled child
            // of a disabled parent will look enabled but not get mouse events
            if (!Enabled)
            {
                cp.Style |= (int)WINDOW_STYLE.WS_DISABLED;
            }

            // If we are being hosted as an Ax control, try to prevent the parking window
            // from being created by pre-filling the window handle here.
            if (cp.Parent == IntPtr.Zero && IsActiveX)
            {
                cp.Parent = ActiveXHWNDParent;
            }

            // Set Rtl bits
            if (RightToLeft == RightToLeft.Yes)
            {
                cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_RTLREADING;
                cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_RIGHT;
                cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_LEFTSCROLLBAR;
            }

            return cp;
        }
    }

    /// <summary>
    ///  Helper method.
    ///
    ///  Triggers validation on the active control, and returns bool indicating whether that control was valid.
    ///
    ///  The correct way to do this is to find the common ancestor of the active control and this control,
    ///  then request validation to be performed by that common container control.
    ///
    ///  Used by controls that don't participate in the normal enter/leave/validation process, but which
    ///  want to force form-level validation to occur before they attempt some important action.
    /// </summary>
    internal bool ValidateActiveControl(out bool validatedControlAllowsFocusChange)
    {
        validatedControlAllowsFocusChange = false;

        if (GetContainerControl() is not ContainerControl container || !CausesValidation)
        {
            return true;
        }

        while (container.ActiveControl is null)
        {
            if (container.ParentInternal is not Control parent
                || parent.GetContainerControl() is not ContainerControl parentContainer)
            {
                break;
            }

            container = parentContainer;
        }

        return container.ValidateInternal(true, out validatedControlAllowsFocusChange);
    }

    internal bool ValidationCancelled
    {
        get => GetState(States.ValidationCancelled) || (ParentInternal is { } parent && parent.ValidationCancelled);
        set => SetState(States.ValidationCancelled, value);
    }

    /// <summary>
    ///  returns bool indicating whether the Top MDI Window is closing.
    ///  This property is set in the MDI children in WmClose method in form.cs when the top window is closing.
    ///  This property will be used in ActiveControl to determine if we want to skip set focus and
    ///  window handle re-creation for the control.
    /// </summary>
    internal bool IsTopMdiWindowClosing
    {
        get => GetExtendedState(ExtendedStates.TopMDIWindowClosing);
        set => SetExtendedState(ExtendedStates.TopMDIWindowClosing, value);
    }

    /// <summary>
    ///  Returns bool indicating whether the control is currently being scaled.
    ///  This property is set in ScaleControl method to allow method being called to condition code that should not run for scaling.
    /// </summary>
    internal bool ScalingInProgress
    {
        get => GetExtendedState(ExtendedStates.CurrentlyBeingScaled);
        private set => SetExtendedState(ExtendedStates.CurrentlyBeingScaled, value);
    }

    /// <summary>
    ///  Indicates whether or not this control has an accessible object associated with it.
    /// </summary>
    internal bool IsAccessibilityObjectCreated => Properties.ContainsKey(s_accessibilityProperty);

    /// <summary>
    ///  Retrieves the Win32 thread ID of the thread that created the
    ///  handle for this control. If the control's handle hasn't been
    ///  created yet, this method will return the current thread's ID.
    /// </summary>
    internal uint CreateThreadId => IsHandleCreated
        ? PInvoke.GetWindowThreadProcessId(this, out _)
        : PInvokeCore.GetCurrentThreadId();

    /// <summary>
    ///  Retrieves the cursor that will be displayed when the mouse is over this
    ///  control.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [SRDescription(nameof(SR.ControlCursorDescr))]
    [AmbientValue(null)]
    [AllowNull]
    public virtual Cursor Cursor
    {
        get
        {
            if (GetState(States.UseWaitCursor))
            {
                return Cursors.WaitCursor;
            }

            if (Properties.TryGetValue(s_cursorProperty, out Cursor? cursor))
            {
                return cursor;
            }

            // We only do ambients for things with "Cursors.Default" as their default.
            Cursor localDefault = DefaultCursor;
            return localDefault != Cursors.Default
                ? localDefault
                : ParentInternal is { } parent ? parent.Cursor : AmbientPropertiesService?.Cursor ?? localDefault;
        }
        set
        {
            Cursor? localCursor = Properties.GetValueOrDefault<Cursor>(s_cursorProperty);
            Cursor resolvedCursor = Cursor;
            if (localCursor != value)
            {
                Properties.AddOrRemoveValue(s_cursorProperty, value);
            }

            // Other things can change the cursor. We always want to force the correct cursor.
            if (IsHandleCreated)
            {
                // We want to instantly change the cursor if the mouse is within our bounds.
                // This includes the case where the mouse is over one of our children.
                PInvoke.GetCursorPos(out Point p);
                PInvokeCore.GetWindowRect(this, out RECT r);
                if ((r.left <= p.X && p.X < r.right && r.top <= p.Y && p.Y < r.bottom) || PInvoke.GetCapture() == HWND)
                {
                    PInvokeCore.SendMessage(this, PInvokeCore.WM_SETCURSOR, (WPARAM)HWND, (LPARAM)(int)PInvoke.HTCLIENT);
                }
            }

            if (!resolvedCursor.Equals(value))
            {
                OnCursorChanged(EventArgs.Empty);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnCursorChangedDescr))]
    public event EventHandler? CursorChanged
    {
        add => Events.AddHandler(s_cursorEvent, value);
        remove => Events.RemoveHandler(s_cursorEvent, value);
    }

    /// <summary>
    ///  Retrieves the bindings for this control.
    /// </summary>
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [SRCategory(nameof(SR.CatData))]
    [SRDescription(nameof(SR.ControlBindingsDescr))]
    [RefreshProperties(RefreshProperties.All)]
    [ParenthesizePropertyName(true)]
    public ControlBindingsCollection DataBindings
    {
        get
        {
            if (!Binding.IsSupported)
            {
                throw new NotSupportedException(SR.BindingNotSupported);
            }

            if (!Properties.TryGetValue(s_bindingsProperty, out ControlBindingsCollection? bindings))
            {
                bindings = Properties.AddValue(s_bindingsProperty, new ControlBindingsCollection(this));
            }

            return bindings;
        }
    }

    /// <summary>
    ///  The default BackColor of a generic top-level Control. Subclasses may have
    ///  different defaults.
    /// </summary>
    public static Color DefaultBackColor => SystemColors.Control;

    /// <summary>
    ///  Deriving classes can override this to configure a default cursor for their control.
    ///  This is more efficient than setting the cursor in the control's constructor,
    ///  and gives automatic support for ShouldSerialize and Reset in the designer.
    /// </summary>
    protected virtual Cursor DefaultCursor => Cursors.Default;

    /// <summary>
    ///  The default Font of a generic top-level Control. Subclasses may have
    ///  different defaults.
    /// </summary>
    public static Font DefaultFont
    {
        get
        {
            if (s_defaultFont is null)
            {
                s_defaultFont = Application.DefaultFont ?? SystemFonts.MessageBoxFont;
                Debug.Assert(s_defaultFont is not null, "defaultFont wasn't set!");
            }

            return s_defaultFont;
        }
    }

    /// <summary>
    ///  The default ForeColor of a generic top-level Control. Subclasses may have
    ///  different defaults.
    /// </summary>
    public static Color DefaultForeColor => SystemColors.ControlText;

    protected virtual Padding DefaultMargin => CommonProperties.DefaultMargin;

    protected virtual Size DefaultMaximumSize => CommonProperties.DefaultMaximumSize;

    protected virtual Size DefaultMinimumSize => CommonProperties.DefaultMinimumSize;

    protected virtual Padding DefaultPadding => Padding.Empty;

    private static RightToLeft DefaultRightToLeft => RightToLeft.No;

    /// <summary>
    ///  Deriving classes can override this to configure a default size for their control.
    ///  This is more efficient than setting the size in the control's constructor.
    /// </summary>
    protected virtual Size DefaultSize => Size.Empty;

    private void DetachContextMenuStrip(object? sender, EventArgs e) => ContextMenuStrip = null;

    /// <summary>
    ///  Dpi value either for the primary screen or for the monitor where the top-level parent is displayed when
    ///  EnableDpiChangedMessageHandling option is on and the application is per-monitor V2 Dpi-aware (rs2+)
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [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;

    // The color to use when drawing disabled text. Normally we use BackColor,
    // but that obviously won't work if we're transparent.
    internal Color DisabledColor
    {
        get
        {
            Color color = BackColor;
            if (color.A != 0)
            {
                return color;
            }

            Control? control = ParentInternal;
            while (color.A == 0)
            {
                if (control is null)
                {
                    // Don't know what to do, this seems good as anything
                    color = SystemColors.Control;
                    break;
                }

                color = control.BackColor;
                control = control.ParentInternal;
            }

            return color;
        }
    }

    /// <summary>
    ///  Returns the client rect of the display area of the control.
    ///  For the base control class, this is identical to getClientRect.
    ///  However, inheriting controls may want to change this if their client
    ///  area differs from their display area.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlDisplayRectangleDescr))]
    public virtual Rectangle DisplayRectangle
        => new(0, 0, _clientWidth, _clientHeight);

    /// <summary>
    ///  Indicates whether the control has been disposed. This
    ///  property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlDisposedDescr))]
    public bool IsDisposed => GetState(States.Disposed);

    /// <summary>
    ///  Disposes of the currently selected font handle (if cached).
    /// </summary>
    private void DisposeFontHandle()
    {
        if (Properties.TryGetValue(s_fontHandleWrapperProperty, out FontHandleWrapper? fontHandle))
        {
            fontHandle.Dispose();
            Properties.RemoveValue(s_fontHandleWrapperProperty);
        }
    }

    /// <summary>
    ///  Indicates whether the control is in the process of being disposed. This
    ///  property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlDisposingDescr))]
    public bool Disposing => GetState(States.Disposing);

    /// <summary>
    ///  The dock property. The dock property controls to which edge
    ///  of the container this control is docked to. For example, when docked to
    ///  the top of the container, the control will be displayed flush at the
    ///  top of the container, extending the length of the container.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    [RefreshProperties(RefreshProperties.Repaint)]
    [DefaultValue(CommonProperties.DefaultDock)]
    [SRDescription(nameof(SR.ControlDockDescr))]
    public virtual DockStyle Dock
    {
        get => DefaultLayout.GetDock(this);
        set
        {
            if (value != Dock)
            {
                using SuspendLayoutScope scope = new(this);
                DefaultLayout.SetDock(this, value);
                OnDockChanged(EventArgs.Empty);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnDockChangedDescr))]
    public event EventHandler? DockChanged
    {
        add => Events.AddHandler(s_dockEvent, value);
        remove => Events.RemoveHandler(s_dockEvent, value);
    }

    /// <summary>
    ///  This will enable or disable double buffering.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ControlDoubleBufferedDescr))]
    protected virtual bool DoubleBuffered
    {
        get => GetStyle(ControlStyles.OptimizedDoubleBuffer);
        set
        {
            if (value != DoubleBuffered)
            {
                if (value)
                {
                    SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, value);
                }
                else
                {
                    SetStyle(ControlStyles.OptimizedDoubleBuffer, value);
                }
            }
        }
    }

    private bool DoubleBufferingEnabled => GetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint);

    /// <summary>
    ///  Indicates whether the control is currently enabled.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Localizable(true)]
    [DispId(PInvokeCore.DISPID_ENABLED)]
    [SRDescription(nameof(SR.ControlEnabledDescr))]
    public bool Enabled
    {
        get
        {
            // We are only enabled if our parent is enabled
            return GetState(States.Enabled) && (ParentInternal is null || ParentInternal.Enabled);
        }
        set
        {
            bool oldValue = Enabled;
            SetState(States.Enabled, value);

            if (oldValue != value)
            {
                if (!value)
                {
                    SelectNextIfFocused();
                }

                OnEnabledChanged(EventArgs.Empty);
            }
        }
    }

    /// <summary>
    ///  Occurs when the control is enabled.
    /// </summary>
    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnEnabledChangedDescr))]
    public event EventHandler? EnabledChanged
    {
        add => Events.AddHandler(s_enabledEvent, value);
        remove => Events.RemoveHandler(s_enabledEvent, value);
    }

    /// <summary>
    ///  Indicates whether the control has focus. This property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlFocusedDescr))]
    public virtual bool Focused
        => IsHandleCreated && PInvoke.GetFocus() == InternalHandle;

    /// <summary>
    ///  Retrieves the current font for this control. This will be the font used
    ///  by default for painting and text in the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [Localizable(true)]
    [DispId(PInvokeCore.DISPID_FONT)]
    [AmbientValue(null)]
    [SRDescription(nameof(SR.ControlFontDescr))]
    [AllowNull]
    public virtual Font Font
    {
        [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ActiveXFontMarshaler))]
        get => GetCurrentFontAndDpi(out _);

        [param: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ActiveXFontMarshaler))]
        set
        {
            Font? previous = Properties.GetValueOrDefault<Font>(s_fontProperty);
            if (Equals(previous, value))
            {
                // Explicitly set font for this control is unchanged, do nothing. Note that this is not
                // reference equality, this is property value equality.
                return;
            }

            Font currentFont = Font;
            Properties.AddOrRemoveValue(s_fontProperty, value);

            // Clear the current cached HFONT, if any.
            DisposeFontHandle();

            // If the value being assigned is the same as the current effective font, we do not need to raise the
            // FontChanged event. Just make sure the WM_SETFONT message is sent.
            if (currentFont.Equals(value))
            {
                if (IsHandleCreated && !GetStyle(ControlStyles.UserPaint))
                {
                    SetWindowFont();
                }

                return;
            }

            if (ScaleHelper.IsThreadPerMonitorV2Aware)
            {
                // Reset the ScaledControlFont value when the font is being set explicitly, in order to keep it
                // in sync when the application is moved between monitors with different Dpi settings.
                ScaledControlFont = null;
                ClearDpiFonts();
            }

            if (Properties.ContainsKey(s_fontHeightProperty))
            {
                Properties.AddValue(s_fontHeightProperty, (value is null) ? -1 : value.Height);
            }

            // Font is an ambient property. We need to layout our parent because Font may
            // change our size. We need to layout ourselves because our children may change
            // size by inheriting the new value.
            using (new LayoutTransaction(ParentInternal, this, PropertyNames.Font))
            {
                OnFontChanged(EventArgs.Empty);
            }
        }
    }

    internal Font GetScaledFont(Font font, int newDpi, int oldDpi)
    {
        Debug.Assert(
            DpiAwarenessContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2),
            $"Fonts need to be cached only for PerMonitorV2 mode applications : {ScaleHelper.IsThreadPerMonitorV2Aware} : {DpiAwarenessContext}");

        _dpiFonts ??= new Dictionary<int, Font>
        {
            { oldDpi, font.WithSize(font.Size) }
        };

        if (_dpiFonts.TryGetValue(newDpi, out Font? scaledFont))
        {
            return scaledFont!;
        }

        float factor = ((float)newDpi / oldDpi);
        scaledFont = font.WithSize(font.Size * factor);

        _dpiFonts.Add(newDpi, scaledFont);

        return scaledFont;
    }

    private void ClearDpiFonts()
    {
        if (_dpiFonts is null)
        {
            return;
        }

        foreach (Font font in _dpiFonts.Values)
        {
            font?.Dispose();
        }

        _dpiFonts.Clear();
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnFontChangedDescr))]
    public event EventHandler? FontChanged
    {
        add => Events.AddHandler(s_fontEvent, value);
        remove => Events.RemoveHandler(s_fontEvent, value);
    }

    internal HFONT FontHandle
    {
        get
        {
            // If application is in PerMonitorV2 mode and font is scaled when application moved between monitor.
            if (ScaledControlFont is not null)
            {
                _scaledFontWrapper ??= new FontHandleWrapper(ScaledControlFont);
                return _scaledFontWrapper.Handle;
            }

            if (TryGetExplicitlySetFont(out Font? font))
            {
                if (!Properties.TryGetValue(s_fontHandleWrapperProperty, out FontHandleWrapper? fontHandle))
                {
                    fontHandle = Properties.AddValue(s_fontHandleWrapperProperty, new FontHandleWrapper(font));
                }

                return fontHandle.Handle;
            }

            if (ParentInternal is { } parent)
            {
                return parent.FontHandle;
            }

            if (AmbientPropertiesService?.Font is { } ambientFont)
            {
                FontHandleWrapper? fontHandle = null;

                if (Properties.TryGetValue(s_currentAmbientFontProperty, out Font? currentAmbient) && currentAmbient == ambientFont)
                {
                    fontHandle = Properties.GetValueOrDefault<FontHandleWrapper?>(s_fontHandleWrapperProperty);
                }
                else
                {
                    Properties.AddValue(s_currentAmbientFontProperty, ambientFont);
                }

                fontHandle ??= Properties.AddValue(s_fontHandleWrapperProperty, new FontHandleWrapper(ambientFont));

                return fontHandle.Handle;
            }

            s_defaultFontHandleWrapper ??= new FontHandleWrapper(DefaultFont);
            return s_defaultFontHandleWrapper.Handle;
        }
    }

    protected int FontHeight
    {
        get
        {
            if (Properties.TryGetValue(s_fontHeightProperty, out int fontHeight) && fontHeight != -1)
            {
                return fontHeight;
            }

            if (TryGetExplicitlySetFont(out Font? font))
            {
                return Properties.AddValue(s_fontHeightProperty, font.Height);
            }

            // Ask the parent if it has the font height.
            int localFontHeight = -1;

            if (ParentInternal is not null && ParentInternal.CanAccessProperties)
            {
                localFontHeight = ParentInternal.FontHeight;
            }

            // If we still have a bad value, then get the actual font height.
            if (localFontHeight == -1)
            {
                localFontHeight = Font.Height;
                Properties.AddValue(s_fontHeightProperty, localFontHeight);
            }

            return localFontHeight;
        }
        set => Properties.AddValue(s_fontHeightProperty, value);
    }

    /// <summary>
    ///  The foreground color of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [DispId(PInvokeCore.DISPID_FORECOLOR)]
    [SRDescription(nameof(SR.ControlForeColorDescr))]
    public virtual Color ForeColor
    {
        get
        {
            Properties.TryGetValue(s_foreColorProperty, out Color color);
            if (!color.IsEmpty)
            {
                return color;
            }

            if (ParentInternal is { } parent && parent.CanAccessProperties)
            {
                return parent.ForeColor;
            }

            if (IsActiveX)
            {
                color = ActiveXAmbientForeColor;
            }

            if (color.IsEmpty)
            {
                AmbientProperties? ambient = AmbientPropertiesService;
                if (ambient is not null)
                {
                    color = ambient.ForeColor;
                }
            }

            return !color.IsEmpty ? color : DefaultForeColor;
        }
        set
        {
            Color previous = ForeColor;
            Properties.AddOrRemoveValue(s_foreColorProperty, value);
            if (!previous.Equals(ForeColor))
            {
                OnForeColorChanged(EventArgs.Empty);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnForeColorChangedDescr))]
    public event EventHandler? ForeColorChanged
    {
        add => Events.AddHandler(s_foreColorEvent, value);
        remove => Events.RemoveHandler(s_foreColorEvent, value);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public virtual Size GetPreferredSize(Size proposedSize)
    {
        if (GetState(States.Disposing | States.Disposed))
        {
            // If someone's asking when we're disposing just return what we last had.
            return CommonProperties.xGetPreferredSizeCache(this);
        }

        // Switch Size.Empty to maximum possible values
        proposedSize = LayoutUtils.ConvertZeroToUnbounded(proposedSize);

        // Force proposedSize to be within the elements constraints. (This applies
        // minimumSize, maximumSize, etc.)
        proposedSize = ApplySizeConstraints(proposedSize);
        if (GetExtendedState(ExtendedStates.UserPreferredSizeCache))
        {
            Size cachedSize = CommonProperties.xGetPreferredSizeCache(this);

            // If the "default" preferred size is being requested, and we have a cached value for it, return it.
            if (!cachedSize.IsEmpty && (proposedSize == LayoutUtils.s_maxSize))
            {
                return cachedSize;
            }
        }

        Size preferredSize;

        CacheTextInternal = true;
        try
        {
            preferredSize = GetPreferredSizeCore(proposedSize);
        }
        finally
        {
            CacheTextInternal = false;
        }

        // There is no guarantee that GetPreferredSizeCore() return something within
        // proposedSize, so we apply the element's constraints again.
        preferredSize = ApplySizeConstraints(preferredSize);

        // If the "default" preferred size was requested, cache the computed value.
        if (GetExtendedState(ExtendedStates.UserPreferredSizeCache) && proposedSize == LayoutUtils.s_maxSize)
        {
            CommonProperties.xSetPreferredSizeCache(this, preferredSize);
        }

        return preferredSize;
    }

    // Overriding this method allows us to get the caching and clamping the proposedSize/output to
    // MinimumSize / MaximumSize from GetPreferredSize for free.
    internal virtual Size GetPreferredSizeCore(Size proposedSize)
    {
        return CommonProperties.GetSpecifiedBounds(this).Size;
    }

    /// <summary>
    ///  The HWND handle that this control is bound to. If the handle
    ///  has not yet been created, this will force handle creation.
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [DispId(PInvokeCore.DISPID_HWND)]
    [SRDescription(nameof(SR.ControlHandleDescr))]
    public IntPtr Handle
    {
        get
        {
            if (CheckForIllegalCrossThreadCalls && !t_inCrossThreadSafeCall && InvokeRequired)
            {
                throw new InvalidOperationException(string.Format(SR.IllegalCrossThreadCall, Name));
            }

            if (!IsHandleCreated)
            {
                CreateHandle();
            }

            return HandleInternal;
        }
    }

    internal IntPtr HandleInternal => _window.Handle;

    internal HWND HWNDInternal => _window.HWND;

    /// <summary>
    ///  True if this control has child controls in its collection. This
    ///  is more efficient than checking for Controls.Count > 0, but has the
    ///  same effect.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlHasChildrenDescr))]
    public bool HasChildren => ChildControls is not null && ChildControls.Count > 0;

    /// <summary>
    ///  The height of this control
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlHeightDescr))]
    public int Height
    {
        get => _height;
        set => SetBounds(_x, _y, _width, value, BoundsSpecified.Height);
    }

    internal unsafe bool HostedInWin32DialogManager
    {
        get
        {
            if (!GetState(States.CheckedHost))
            {
                Control topMost = TopMostParent;
                if (this != topMost)
                {
                    SetState(States.HostedInDialog, topMost.HostedInWin32DialogManager);
                }
                else
                {
                    HWND parentHandle = PInvoke.GetParent(this);
                    HWND lastParentHandle = parentHandle;
                    SetState(States.HostedInDialog, false);
                    Span<char> buffer = stackalloc char[PInvokeCore.MaxClassName];

                    while (!parentHandle.IsNull)
                    {
                        int length = 0;
                        fixed (char* lpClassName = buffer)
                        {
                            length = PInvoke.GetClassName(lastParentHandle, lpClassName, buffer.Length);
                        }

                        // #32770 is the standard windows dialog class name
                        // https://learn.microsoft.com/windows/win32/winmsg/about-window-classes#system-classes
                        ReadOnlySpan<char> className = "#32770";
                        if (className.Equals(buffer[..length], StringComparison.Ordinal))
                        {
                            SetState(States.HostedInDialog, true);
                            break;
                        }

                        lastParentHandle = parentHandle;
                        parentHandle = PInvoke.GetParent(parentHandle);
                    }
                }

                SetState(States.CheckedHost, true);
            }

            return GetState(States.HostedInDialog);
        }
    }

    /// <summary>
    ///  Whether or not this control has a handle associated with it.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlHandleCreatedDescr))]
    public bool IsHandleCreated => _window.Handle != 0;

    private protected virtual bool IsHoveredWithMouse() => ClientRectangle.Contains(PointToClient(MousePosition));

    /// <summary>
    ///  Determines if layout is currently suspended.
    /// </summary>
    internal bool IsLayoutSuspended => LayoutSuspendCount > 0;

    internal bool IsWindowObscured
    {
        get
        {
            if (!IsHandleCreated || !Visible)
            {
                return false;
            }

            Control? parent = ParentInternal;
            if (parent is not null)
            {
                while (parent.ParentInternal is not null)
                {
                    parent = parent.ParentInternal;
                }
            }

            PInvokeCore.GetWindowRect(this, out var temp);
            using Region working = new(temp);

            HWND prev;
            HWND next;
            HWND start = parent is not null ? parent.HWND : HWND;

            for (prev = start;
                !(next = PInvoke.GetWindow(prev, GET_WINDOW_CMD.GW_HWNDPREV)).IsNull;
                prev = next)
            {
                PInvokeCore.GetWindowRect(next, out temp);
                if (PInvoke.IsWindowVisible(next))
                {
                    working.Exclude(temp);
                }
            }

            using Graphics g = CreateGraphics();
            return working.IsEmpty(g);
        }
    }

    /// <summary>
    ///  Returns the current value of the handle. This may be zero if the handle
    ///  has not been created.
    /// </summary>
    internal HWND InternalHandle => !IsHandleCreated ? default : (HWND)Handle;

    /// <summary>
    ///  Determines if the caller must call invoke when making method
    ///  calls to this control. Controls in windows forms are bound to a specific thread,
    ///  and are not thread safe. Therefore, if you are calling a control's method
    ///  from a different thread, you must use the control's invoke method
    ///  to marshal the call to the proper thread. This function can be used to
    ///  determine if you must call invoke, which can be handy if you don't know
    ///  what thread owns a control.
    ///
    ///  There are five functions on a control that are safe to call from any
    ///  thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and
    ///  CreateGraphics. For all other method calls, you should use one of the
    ///  invoke methods.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlInvokeRequiredDescr))]
    public bool InvokeRequired
    {
        get
        {
            using var scope = MultithreadSafeCallScope.Create();

            Control control;
            if (IsHandleCreated)
            {
                control = this;
            }
            else
            {
                Control marshalingControl = FindMarshalingControl();

                if (!marshalingControl.IsHandleCreated)
                {
                    return false;
                }

                control = marshalingControl;
            }

            return PInvoke.GetWindowThreadProcessId(control, out _) != PInvokeCore.GetCurrentThreadId();
        }
    }

    /// <summary>
    ///  Indicates whether or not this control is an accessible control
    ///  i.e. whether it should be visible to accessibility applications.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlIsAccessibleDescr))]
    public bool IsAccessible
    {
        get => GetState(States.IsAccessible);
        set => SetState(States.IsAccessible, value);
    }

    /// <summary>
    ///  Used to tell if this control is being hosted as an ActiveX control.
    /// </summary>
    internal bool IsActiveX => GetExtendedState(ExtendedStates.IsActiveX);

    /// <summary>
    ///  Indicates if one of the Ancestors of this control is sited
    ///  and that site in DesignMode. This property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public bool IsAncestorSiteInDesignMode =>
        GetSitedParentSite(this) is ISite parentSite && parentSite.DesignMode;

    private static ISite? GetSitedParentSite(Control control)
    {
        ArgumentNullException.ThrowIfNull(control);
        return (control.Site is not null && control.Site.DesignMode) || control.Parent is null ?
            control.Site : GetSitedParentSite(control.Parent);
    }

    // If the control on which GetContainerControl( ) is called is a ContainerControl, then we don't return the parent
    // but return the same control. This is Everett behavior so we cannot change this since this would be a breaking change.
    // Hence we have a new internal property IsContainerControl which returns false for all Everett control, but
    // this property is overridden in SplitContainer to return true so that we skip the SplitContainer
    // and the correct Parent ContainerControl is returned by GetContainerControl().
    internal virtual bool IsContainerControl => false;

    /// <summary>
    ///  Used to tell if the control is mirrored
    ///  Don't call this from CreateParams. Will lead to nasty problems
    ///  since we might call CreateParams here - you dig!
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.IsMirroredDescr))]
    public bool IsMirrored
    {
        get
        {
            if (!IsHandleCreated)
            {
                CreateParams cp = CreateParams;
                SetState(States.Mirrored, (cp.ExStyle & (int)WINDOW_EX_STYLE.WS_EX_LAYOUTRTL) != 0);
            }

            return GetState(States.Mirrored);
        }
    }

    /// <summary>
    ///  Specifies whether the control is willing to process mnemonics when hosted in an container ActiveX (Ax Sourcing).
    /// </summary>
    internal virtual bool IsMnemonicsListenerAxSourced => false;

    /// <summary>
    ///  Used to tell if this BackColor is Supported
    /// </summary>
    private bool IsValidBackColor(Color c) => c.IsEmpty || GetStyle(ControlStyles.SupportsTransparentBackColor) || c.A == 255;

    /// <summary>
    ///  Stores information about the last button or combination pressed by the user.
    /// </summary>
    private protected static Keys LastKeyData { get; set; }

    /// <summary>
    ///  The left coordinate of this control.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlLeftDescr))]
    public int Left
    {
        get => _x;
        set => SetBounds(value, _y, _width, _height, BoundsSpecified.X);
    }

    /// <summary>
    ///  The location of this control.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlLocationDescr))]
    public Point Location
    {
        get => new(_x, _y);
        set => SetBounds(value.X, value.Y, _width, _height, BoundsSpecified.Location);
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnLocationChangedDescr))]
    public event EventHandler? LocationChanged
    {
        add => Events.AddHandler(s_locationEvent, value);
        remove => Events.RemoveHandler(s_locationEvent, value);
    }

    [SRDescription(nameof(SR.ControlMarginDescr))]
    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    public Padding Margin
    {
        get => CommonProperties.GetMargin(this);
        set
        {
            // This should be done here rather than in the property store as
            // some IArrangedElements actually support negative padding.
            value = LayoutUtils.ClampNegativePaddingToZero(value);

            // SetMargin causes a layout as a side effect.
            if (value != Margin)
            {
                CommonProperties.SetMargin(this, value);
                OnMarginChanged(EventArgs.Empty);
            }

            Debug.Assert(Margin == value, "Error detected while setting Margin.");
        }
    }

    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnMarginChangedDescr))]
    public event EventHandler? MarginChanged
    {
        add => Events.AddHandler(s_marginChangedEvent, value);
        remove => Events.RemoveHandler(s_marginChangedEvent, value);
    }

    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlMaximumSizeDescr))]
    [AmbientValue(typeof(Size), "0, 0")]
    public virtual Size MaximumSize
    {
        get { return CommonProperties.GetMaximumSize(this, DefaultMaximumSize); }
        set
        {
            if (value == Size.Empty)
            {
                CommonProperties.ClearMaximumSize(this);
                Debug.Assert(MaximumSize == DefaultMaximumSize, "Error detected while resetting MaximumSize.");
            }
            else if (value != MaximumSize)
            {
                // SetMaximumSize causes a layout as a side effect.
                CommonProperties.SetMaximumSize(this, value);
                Debug.Assert(MaximumSize == value, "Error detected while setting MaximumSize.");
            }
        }
    }

    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlMinimumSizeDescr))]
    public virtual Size MinimumSize
    {
        get { return CommonProperties.GetMinimumSize(this, DefaultMinimumSize); }
        set
        {
            if (value != MinimumSize)
            {
                // SetMinimumSize causes a layout as a side effect.
                CommonProperties.SetMinimumSize(this, value);
            }

            Debug.Assert(MinimumSize == value, "Error detected while setting MinimumSize.");
        }
    }

    /// <summary>
    ///  Retrieves the current state of the modifier keys. This will check the
    ///  current state of the shift, control, and alt keys.
    /// </summary>
    public static Keys ModifierKeys
    {
        get
        {
            Keys modifiers = 0;

            if (PInvoke.GetKeyState((int)Keys.ShiftKey) < 0)
            {
                modifiers |= Keys.Shift;
            }

            if (PInvoke.GetKeyState((int)Keys.ControlKey) < 0)
            {
                modifiers |= Keys.Control;
            }

            if (PInvoke.GetKeyState((int)Keys.Menu) < 0)
            {
                modifiers |= Keys.Alt;
            }

            return modifiers;
        }
    }

    /// <summary>
    ///  The current state of the mouse buttons. This will check the
    ///  current state of the left, right, and middle mouse buttons.
    /// </summary>
    public static MouseButtons MouseButtons
    {
        get
        {
            MouseButtons buttons = default;

            if (PInvoke.GetKeyState((int)Keys.LButton) < 0)
            {
                buttons |= MouseButtons.Left;
            }

            if (PInvoke.GetKeyState((int)Keys.RButton) < 0)
            {
                buttons |= MouseButtons.Right;
            }

            if (PInvoke.GetKeyState((int)Keys.MButton) < 0)
            {
                buttons |= MouseButtons.Middle;
            }

            if (PInvoke.GetKeyState((int)Keys.XButton1) < 0)
            {
                buttons |= MouseButtons.XButton1;
            }

            if (PInvoke.GetKeyState((int)Keys.XButton2) < 0)
            {
                buttons |= MouseButtons.XButton2;
            }

            return buttons;
        }
    }

    /// <summary>
    ///  The current position of the mouse in screen coordinates.
    /// </summary>
    public static Point MousePosition
    {
        get
        {
            PInvoke.GetCursorPos(out Point pt);
            return pt;
        }
    }

    /// <summary>
    ///  Name of this control. The designer will set this to the same
    ///  as the programmatic Id "(name)" of the control. The name can be
    ///  used as a key into the ControlCollection.
    /// </summary>
    [Browsable(false)]
    [AllowNull]
    public string Name
    {
        get
        {
            string? name = Properties.GetValueOrDefault<string>(s_namePropertyProperty);
            if (string.IsNullOrEmpty(name) && Site is not null)
            {
                name = Site.Name;
            }

            return name ?? string.Empty;
        }
        set => Properties.AddOrRemoveString(s_namePropertyProperty, value);
    }

    /// <summary>
    ///  The parent of this control.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlParentDescr))]
    public Control? Parent
    {
        get => ParentInternal;
        set => ParentInternal = value;
    }

    internal virtual Control? ParentInternal
    {
        get => _parent;
        set
        {
            if (_parent == value)
            {
                return;
            }

            if (value is not null)
            {
                value.Controls.Add(this);
            }
            else
            {
                _parent?.Controls.Remove(this);
            }
        }
    }

    /// <summary>
    ///  Retrieves the product name of this specific component.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlProductNameDescr))]
    public string ProductName => VersionInfo.ProductName;

    /// <summary>
    ///  Retrieves the product version of this specific component.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlProductVersionDescr))]
    public string ProductVersion => VersionInfo.ProductVersion;

    /// <summary>
    ///  Retrieves our internal property storage object. If you have a property
    ///  whose value is not always set, you should store it in here to save space.
    /// </summary>
    internal PropertyStore Properties { get; }

    // Returns the value of the backColor field -- no asking the parent with its color is, etc.
    internal Color RawBackColor => Properties.GetValueOrDefault<Color>(s_backColorProperty);

    /// <summary>
    ///  Indicates whether the control is currently recreating its handle. This
    ///  property is read-only.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlRecreatingHandleDescr))]
    public bool RecreatingHandle => GetState(States.Recreate);

    internal virtual void AddReflectChild()
    {
    }

    internal virtual void RemoveReflectChild()
    {
    }

    internal virtual void RemoveToolTip(ToolTip toolTip)
    {
        // Control doesn't have a specific logic after a toolTip is removed
    }

    private Control? ReflectParent
    {
        get => _reflectParent?.TryGetTarget(out Control? parent) ?? false ? parent : null;
        set
        {
            value?.AddReflectChild();

            Control? existing = ReflectParent;
            _reflectParent = value is not null ? new(value) : null;
            existing?.RemoveReflectChild();
        }
    }

    /// <summary>
    ///  The Region associated with this control. (defines the
    ///  outline/silhouette/boundary of control)
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlRegionDescr))]
    public Region? Region
    {
        get => Properties.GetValueOrDefault<Region>(s_regionProperty);
        set
        {
            Region? oldRegion = SetRegionInternal(value);
            if (oldRegion != value)
            {
                oldRegion?.Dispose();
                OnRegionChanged(EventArgs.Empty);
            }
        }
    }

    private Region? SetRegionInternal(Region? region)
    {
        Region? oldRegion = Properties.AddOrRemoveValue(s_regionProperty, region);

        if (oldRegion == region || !IsHandleCreated)
        {
            // We'll get called when OnHandleCreated runs.
            return oldRegion;
        }

        if (region is null)
        {
            PInvoke.SetWindowRgn(this, default, PInvoke.IsWindowVisible(this));
            return oldRegion;
        }

        // If we're an ActiveX control, clone the region so it can potentially be modified
        using Region? regionCopy = IsActiveX ? ActiveXMergeRegion(region.Clone()) : null;
        using RegionScope regionHandle = (regionCopy ?? region).GetRegionScope(HWND);

        if (PInvoke.SetWindowRgn(this, regionHandle, PInvoke.IsWindowVisible(this)) != 0)
        {
            // Success, the window now owns the region
            regionHandle.RelinquishOwnership();
        }

        return oldRegion;
    }

    /// <summary>
    ///  Event fired when the value of Region property is changed on Control
    /// </summary>
    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlRegionChangedDescr))]
    public event EventHandler? RegionChanged
    {
        add => Events.AddHandler(s_regionChangedEvent, value);
        remove => Events.RemoveHandler(s_regionChangedEvent, value);
    }

    // Helper function for Rtl
    [Obsolete("This property has been deprecated. Please use RightToLeft instead. https://go.microsoft.com/fwlink/?linkid=14202")]
    protected internal bool RenderRightToLeft => true;

    /// <summary>
    ///  Determines if the parent's background will be rendered on the label control.
    /// </summary>
    internal bool RenderTransparent
        => GetStyle(ControlStyles.SupportsTransparentBackColor) && BackColor.A < 255;

    private bool RenderColorTransparent(Color c)
        => GetStyle(ControlStyles.SupportsTransparentBackColor) && c.A < 255;

    /// <summary>
    ///  This property is required by certain controls (TabPage) to render its transparency using theming API.
    ///  We don't want all controls (that are have transparent BackColor) to use theming API to render its background
    ///  because it has  HUGE PERF cost.
    /// </summary>
    internal virtual bool RenderTransparencyWithVisualStyles => false;

    /// <summary>
    ///  Represents the bounds of the control that need to be scaled. Control bounds
    ///  need to be scaled until ScaleControl is called. They need to be scaled again
    ///  if their bounds change after ScaleControl is called.
    /// </summary>
    internal BoundsSpecified RequiredScaling
    {
        get => (_requiredScaling & RequiredScalingEnabledMask) != 0
            ? (BoundsSpecified)(_requiredScaling & RequiredScalingMask)
            : BoundsSpecified.None;
        set
        {
            byte enableBit = (byte)(_requiredScaling & RequiredScalingEnabledMask);
            _requiredScaling = (byte)(((int)value & RequiredScalingMask) | enableBit);
        }
    }

    /// <summary>
    ///  Determines if the required scaling property is enabled. If not,
    ///  RequiredScaling always returns None.
    /// </summary>
    internal bool RequiredScalingEnabled
    {
        get => (_requiredScaling & RequiredScalingEnabledMask) != 0;
        set
        {
            byte scaling = (byte)(_requiredScaling & RequiredScalingMask);
            _requiredScaling = scaling;
            if (value)
            {
                _requiredScaling |= RequiredScalingEnabledMask;
            }
        }
    }

    /// <summary>
    ///  Indicates whether the control should redraw itself when resized.
    /// </summary>
    [SRDescription(nameof(SR.ControlResizeRedrawDescr))]
    protected bool ResizeRedraw
    {
        get => GetStyle(ControlStyles.ResizeRedraw);
        set => SetStyle(ControlStyles.ResizeRedraw, value);
    }

    /// <summary>
    ///  The right coordinate of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlRightDescr))]
    public int Right => _x + _width;

    /// <summary>
    ///  This is used for international applications where the language
    ///  is written from RightToLeft. When this property is true,
    ///  control placement and text will be from right to left.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [Localizable(true)]
    [AmbientValue(RightToLeft.Inherit)]
    [SRDescription(nameof(SR.ControlRightToLeftDescr))]
    public virtual RightToLeft RightToLeft
    {
        get
        {
            if (!Properties.TryGetValue(s_rightToLeftProperty, out RightToLeft rightToLeft)
                || rightToLeft == RightToLeft.Inherit)
            {
                rightToLeft = ParentInternal?.RightToLeft ?? DefaultRightToLeft;
            }

            return rightToLeft;
        }
        set
        {
            // valid values are 0x0 to 0x2.
            SourceGenerated.EnumValidator.Validate(value);

            RightToLeft oldValue = RightToLeft;

            if (Properties.ContainsKey(s_rightToLeftProperty) || value != RightToLeft.Inherit)
            {
                Properties.AddValue(s_rightToLeftProperty, value);
            }

            if (oldValue != RightToLeft)
            {
                // Setting RTL on a container does not cause the container to change size.
                // Only the children need to have their layout updated.
                using (new LayoutTransaction(this, this, PropertyNames.RightToLeft))
                {
                    OnRightToLeftChanged(EventArgs.Empty);
                }
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnRightToLeftChangedDescr))]
    public event EventHandler? RightToLeftChanged
    {
        add => Events.AddHandler(s_rightToLeftEvent, value);
        remove => Events.RemoveHandler(s_rightToLeftEvent, value);
    }

    /// <summary>
    ///  This property controls the scaling of child controls. If true child controls
    ///  will be scaled when the Scale method on this control is called. If false,
    ///  child controls will not be scaled. The default is true, and you must override
    ///  this property to provide a different value.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual bool ScaleChildren => true;

    /// <summary>
    ///  Stores scaled font from Dpi changed values. This is required to distinguish the Font change from
    ///  Dpi changed events and explicit Font change/assignment. Caching Font values for each Dpi is complex.
    ///  ToDo: Look into caching Dpi and control bounds for each Dpi to improve perf.
    ///  https://github.com/dotnet/winforms/issues/5047
    /// </summary>
    internal Font? ScaledControlFont
    {
        get => _scaledControlFont;
        set
        {
            _scaledFontWrapper?.Dispose();
            _scaledFontWrapper = null;

            _scaledControlFont = value;
            if (Properties.ContainsKey(s_fontHeightProperty))
            {
                Properties.AddValue(s_fontHeightProperty, (value is null) ? -1 : value.Height);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public override ISite? Site
    {
        get => base.Site;
        set
        {
            AmbientProperties? oldAmbients = AmbientPropertiesService;
            AmbientProperties? newAmbients = null;

            if (value is not null)
            {
                newAmbients = value.GetService<AmbientProperties>();
            }

            // If the ambients changed, compare each property.
            if (oldAmbients != newAmbients)
            {
                bool checkFont = !Properties.ContainsKey(s_fontProperty);
                bool checkBackColor = !Properties.ContainsKey(s_backColorProperty);
                bool checkForeColor = !Properties.ContainsKey(s_foreColorProperty);
                bool checkCursor = !Properties.ContainsKey(s_cursorProperty);

                Font? oldFont = null;
                Color oldBackColor = Color.Empty;
                Color oldForeColor = Color.Empty;
                Cursor? oldCursor = null;

                if (checkFont)
                {
                    oldFont = Font;
                }

                if (checkBackColor)
                {
                    oldBackColor = BackColor;
                }

                if (checkForeColor)
                {
                    oldForeColor = ForeColor;
                }

                if (checkCursor)
                {
                    oldCursor = Cursor;
                }

                Properties.AddValue(s_ambientPropertiesServiceProperty, newAmbients);
                base.Site = value;

                if (checkFont && !oldFont!.Equals(Font))
                {
                    OnFontChanged(EventArgs.Empty);
                }

                if (checkForeColor && !oldForeColor.Equals(ForeColor))
                {
                    OnForeColorChanged(EventArgs.Empty);
                }

                if (checkBackColor && !oldBackColor.Equals(BackColor))
                {
                    OnBackColorChanged(EventArgs.Empty);
                }

                if (checkCursor && oldCursor!.Equals(Cursor))
                {
                    OnCursorChanged(EventArgs.Empty);
                }
            }
            else
            {
                // If the ambients haven't changed, we just set a new site.
                base.Site = value;
            }
        }
    }

    /// <summary>
    ///  The size of the control.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlSizeDescr))]
    public Size Size
    {
        get => new(_width, _height);
        set => SetBounds(_x, _y, value.Width, value.Height, BoundsSpecified.Size);
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnSizeChangedDescr))]
    public event EventHandler? SizeChanged
    {
        add => Events.AddHandler(s_sizeEvent, value);
        remove => Events.RemoveHandler(s_sizeEvent, value);
    }

    /// <summary>
    ///  The tab index of this control.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Localizable(true)]
    [MergableProperty(false)]
    [SRDescription(nameof(SR.ControlTabIndexDescr))]
    public int TabIndex
    {
        get => _tabIndex == -1 ? 0 : _tabIndex;
        set
        {
            ArgumentOutOfRangeException.ThrowIfNegative(value);

            if (_tabIndex != value)
            {
                _tabIndex = value;
                OnTabIndexChanged(EventArgs.Empty);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnTabIndexChangedDescr))]
    public event EventHandler? TabIndexChanged
    {
        add => Events.AddHandler(s_tabIndexEvent, value);
        remove => Events.RemoveHandler(s_tabIndexEvent, value);
    }

    /// <summary>
    ///  Indicates whether the user can give the focus to this control using the TAB
    ///  key. This property is read-only.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [DefaultValue(true)]
    [DispId(PInvokeCore.DISPID_TABSTOP)]
    [SRDescription(nameof(SR.ControlTabStopDescr))]
    public bool TabStop
    {
        get => TabStopInternal;
        set
        {
            if (TabStop != value)
            {
                TabStopInternal = value;
                if (IsHandleCreated)
                {
                    SetWindowStyle((int)WINDOW_STYLE.WS_TABSTOP, value);
                }

                OnTabStopChanged(EventArgs.Empty);
            }
        }
    }

    // Grab out the logical of setting TABSTOP state, so that derived class could use this.
    internal bool TabStopInternal
    {
        get => (_state & States.TabStop) != 0;
        set
        {
            if (TabStopInternal != value)
            {
                SetState(States.TabStop, value);
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnTabStopChangedDescr))]
    public event EventHandler? TabStopChanged
    {
        add => Events.AddHandler(s_tabStopEvent, value);
        remove => Events.RemoveHandler(s_tabStopEvent, value);
    }

    [SRCategory(nameof(SR.CatData))]
    [Localizable(false)]
    [Bindable(true)]
    [SRDescription(nameof(SR.ControlTagDescr))]
    [DefaultValue(null)]
    [TypeConverter(typeof(StringConverter))]
    public object? Tag
    {
        get => Properties.GetValueOrDefault<object>(s_userDataProperty);
        set => Properties.AddOrRemoveValue(s_userDataProperty, value);
    }

    /// <summary>
    ///  The current text associated with this control.
    /// </summary>
    [SRCategory(nameof(SR.CatAppearance))]
    [Localizable(true)]
    [Bindable(true)]
    [DispId(PInvokeCore.DISPID_TEXT)]
    [SRDescription(nameof(SR.ControlTextDescr))]
    [AllowNull]
    public virtual string Text
    {
        get => CacheTextInternal ? _text ?? string.Empty : WindowText;
        set
        {
            value ??= string.Empty;

            if (value == Text)
            {
                return;
            }

            if (CacheTextInternal)
            {
                _text = value;
            }

            WindowText = value;
            OnTextChanged(EventArgs.Empty);

            if (IsMnemonicsListenerAxSourced)
            {
                for (Control? control = this; control is not null; control = control.ParentInternal)
                {
                    if (control.IsActiveX && control.Properties.TryGetValue(s_activeXImplProperty, out ActiveXImpl? activeX))
                    {
                        activeX.UpdateAccelTable();
                        break;
                    }
                }
            }
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnTextChangedDescr))]
    public event EventHandler? TextChanged
    {
        add => Events.AddHandler(s_textEvent, value);
        remove => Events.RemoveHandler(s_textEvent, value);
    }

    /// <summary>
    ///  Top coordinate of this control.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlTopDescr))]
    public int Top
    {
        get => _y;
        set => SetBounds(_x, value, _width, _height, BoundsSpecified.Y);
    }

    /// <summary>
    ///  The top level control that contains this control. This doesn't
    ///  have to be the same as the value returned from getForm since forms
    ///  can be parented to other controls.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlTopLevelControlDescr))]
    public Control? TopLevelControl => TopLevelControlInternal;

    internal Control? TopLevelControlInternal
    {
        get
        {
            Control? control = this;
            while (control is not null && !control.GetTopLevel())
            {
                control = control.ParentInternal;
            }

            return control;
        }
    }

    internal Control TopMostParent
    {
        get
        {
            Control control = this;
            while (control.ParentInternal is not null)
            {
                control = control.ParentInternal;
            }

            return control;
        }
    }

    // This auto upgraded v1 client to per-process double buffering logic
    private static BufferedGraphicsContext BufferContext => BufferedGraphicsManager.Current;

    /// <summary>
    ///  Indicates whether the user interface is in a state to show or hide keyboard
    ///  accelerators. This property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected internal virtual bool ShowKeyboardCues
    {
        get
        {
            // Controls in design mode always draw their accelerators.
            if (!IsHandleCreated || DesignMode)
            {
                return true;
                // would be nice to query SystemParametersInfo, but have trouble
                // getting this to work and this should not really be called before
                // handle created anyway
            }

            // How this all works

            // uiCuesState contains this control's cached state of whether or not it thinks
            // accelerators/focus cues are turned on. the first 16 bits represent focus cues
            // the second represent keyboard cues. "F" is the UICuesStates.FocusMask,
            // "F0" is the UICuesStates.KeyboardMask

            // We check here if we have cached state. If we don't, we need to initialize ourself.
            // We do this by checking "MenuAccessKeysUnderlined" - we show if this returns true.

            // If MenuAccessKeysUnderlined returns false, we have to manually call CHANGEUISTATE on the topmost control
            // Why? Well the way the API seems to work is that it stores in a bit flag for the hidden
            // state.

            // Details from the Menu keyDown to changed value of _uiCuesState.

            // When someone does press the ALT (Menu)/F10 key we will
            //   Call ProcessUICues on the control that had focus at the time
            //          ProcessUICues will check the current state of the control using WM_QUERYUISTATE
            //          If WM_QUERYUISTATE indicates that the accelerators are hidden we will
            //                  either call WM_UPDATEUISTATE or WM_CHANGEUISTATE depending on whether we're hosted or not.
            //          All controls in the hierarchy will be individually called back on WM_UPDATEUISTATE,
            //          which will go into WmUpdateUIState.
            //   In WmUpdateUIState, we will update our uiCuesState cached value, which
            //   changes the public value of what we return here for ShowKeyboardCues/ShowFocusCues.

            if ((_uiCuesState & UICuesStates.KeyboardMask) == 0)
            {
                if (SystemInformation.MenuAccessKeysUnderlined)
                {
                    _uiCuesState |= UICuesStates.KeyboardShow;
                }
                else
                {
                    // if we're in the hidden state, we need to manufacture an update message so everyone knows it.
                    uint actionMask = PInvoke.UISF_HIDEACCEL << 16;
                    _uiCuesState |= UICuesStates.KeyboardHidden;

                    // The side effect of this initial state is that adding new controls may clear the accelerator
                    // state (has been this way forever)
                    PInvokeCore.SendMessage(
                        TopMostParent,
                        PInvokeCore.WM_CHANGEUISTATE,
                        (WPARAM)(actionMask | PInvoke.UIS_SET));
                }
            }

            return (_uiCuesState & UICuesStates.KeyboardMask) == UICuesStates.KeyboardShow;
        }
    }

    /// <summary>
    ///  Indicates whether the user interface is in a state to show or hide focus
    ///  rectangles. This property is read-only.
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected internal virtual unsafe bool ShowFocusCues
    {
        get
        {
            if (!IsHandleCreated)
            {
                return true;
                // would be nice to query SystemParametersInfo, but have trouble
                // getting this to work and this should not really be called before
                // handle created anyway
            }

            // See "How this all works" in ShowKeyboardCues

            if ((_uiCuesState & UICuesStates.FocusMask) == 0)
            {
                if (SystemInformation.MenuAccessKeysUnderlined)
                {
                    _uiCuesState |= UICuesStates.FocusShow;
                }
                else
                {
                    _uiCuesState |= UICuesStates.FocusHidden;

                    // if we're in the hidden state, we need to manufacture an update message so everyone knows it.
                    int actionMask = (int)(PInvoke.UISF_HIDEACCEL | PInvoke.UISF_HIDEFOCUS) << 16;

                    // The side effect of this initial state is that adding new controls may clear the focus cue state
                    // state (has been this way forever)
                    PInvokeCore.SendMessage(TopMostParent,
                        PInvokeCore.WM_CHANGEUISTATE,
                        (WPARAM)(actionMask | (int)PInvoke.UIS_SET));
                }
            }

            return (_uiCuesState & UICuesStates.FocusMask) == UICuesStates.FocusShow;
        }
    }

    // The parameter used in the call to ShowWindow for this control
    internal virtual SHOW_WINDOW_CMD ShowParams => SHOW_WINDOW_CMD.SW_SHOW;

    /// <summary>
    ///  When this property in true the Cursor Property is set to WaitCursor as well as the Cursor Property
    ///  of all the child controls.
    /// </summary>
    [DefaultValue(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [Browsable(true)]
    [SRCategory(nameof(SR.CatAppearance))]
    [SRDescription(nameof(SR.ControlUseWaitCursorDescr))]
    public bool UseWaitCursor
    {
        get => GetState(States.UseWaitCursor);
        set
        {
            if (GetState(States.UseWaitCursor) != value)
            {
                SetState(States.UseWaitCursor, value);

                if (ChildControls is { } children)
                {
                    for (int i = 0; i < children.Count; i++)
                    {
                        children[i].UseWaitCursor = value;
                    }
                }
            }
        }
    }

    /// <summary>
    ///  Determines whether to use compatible text rendering engine (GDI+) or not (GDI).
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This property overrides <see cref="UseCompatibleTextRenderingDefault"/>.
    ///  </para>
    ///  <para>
    ///   Exposed publicly only by controls that support GDI text rendering (<see cref="Label"/>, <see cref="LinkLabel"/>
    ///   and some others).
    ///  </para>
    ///  <para>
    ///   Observe that this property is NOT virtual (to allow for caching the property value - see <see cref="LinkLabel"/>)
    ///   and should be used by controls that support it only (see <see cref="SupportsUseCompatibleTextRendering"/>).
    ///  </para>
    /// </remarks>
    internal bool UseCompatibleTextRenderingInternal
    {
        get => Properties.TryGetValue(s_useCompatibleTextRenderingProperty, out bool value)
            ? value
            : UseCompatibleTextRenderingDefault;
        set
        {
            if (SupportsUseCompatibleTextRendering && UseCompatibleTextRenderingInternal != value)
            {
                Properties.AddValue(s_useCompatibleTextRenderingProperty, value);

                // Update the preferred size cache since we will be rendering text using a different engine.
                LayoutTransaction.DoLayoutIf(AutoSize, ParentInternal, this, PropertyNames.UseCompatibleTextRendering);
                Invalidate();
            }
        }
    }

    /// <summary>
    ///  Determines whether the control supports rendering text using GDI+ and GDI.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This is provided for container controls (PropertyGrid) to iterate through its children to set
    ///   <see cref="UseCompatibleTextRenderingInternal"/> to the same value if the child control supports it.
    ///  </para>
    /// </remarks>
    internal virtual bool SupportsUseCompatibleTextRendering => false;

    private ControlVersionInfo VersionInfo
    {
        get
        {
            if (!Properties.TryGetValue(s_controlVersionInfoProperty, out ControlVersionInfo? info))
            {
                info = new(this);
                Properties.AddValue(s_controlVersionInfoProperty, info);
            }

            return info;
        }
    }

    /// <summary>
    ///  Indicates whether the control is visible.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlVisibleDescr))]
    public bool Visible
    {
        get
        {
            if (!DesiredVisibility)
            {
                return false;
            }

            // We are only visible if our parent is visible
            return ParentInternal is null || ParentInternal.Visible;
        }
        set => SetVisibleCore(value);
    }

    /// <summary>
    ///  Occurs when the control becomes visible.
    /// </summary>
    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnVisibleChangedDescr))]
    public event EventHandler? VisibleChanged
    {
        add => Events.AddHandler(s_visibleEvent, value);
        remove => Events.RemoveHandler(s_visibleEvent, value);
    }

    /// <summary>
    ///  Wait for the wait handle to receive a signal: throw an exception if the thread is no longer with us.
    /// </summary>
    private unsafe void WaitForWaitHandle(WaitHandle waitHandle)
    {
        uint threadId = CreateThreadId;
        Application.ThreadContext? ctx = Application.ThreadContext.FromId(threadId);
        if (ctx is null)
        {
            // Couldn't find the thread context, so we don't know the state. We shouldn't throw.
            return;
        }

        HANDLE threadHandle = ctx.Handle;
        bool processed = false;

        // setting default exitcode to 0, though it won't be accessed in current code below due to short-circuit logic
        // in condition (returnValue will be false when exitCode is undefined)
        uint exitCode = 0;
        bool returnValue = false;
        while (!processed)
        {
            // Get the thread's exit code, if we found the thread as expected
            if (threadHandle != 0)
            {
                returnValue = PInvoke.GetExitCodeThread(threadHandle, &exitCode);
            }

            // If we didn't find the thread, or if GetExitCodeThread failed, we don't know the thread's state:
            // if we don't know, we shouldn't throw.
            if ((returnValue && exitCode != NTSTATUS.STILL_ACTIVE)
                || (!returnValue && Marshal.GetLastWin32Error() == (int)WIN32_ERROR.ERROR_INVALID_HANDLE)
                || AppDomain.CurrentDomain.IsFinalizingForUnload())
            {
                if (waitHandle.WaitOne(1, false))
                {
                    break;
                }

                throw new InvalidAsynchronousStateException(SR.ThreadNoLongerValid);
            }

            if (IsDisposed && _threadCallbackList is not null && _threadCallbackList.Count > 0)
            {
                lock (_threadCallbackList)
                {
                    Exception ex = new ObjectDisposedException(GetType().Name);
                    while (_threadCallbackList.Count > 0)
                    {
                        ThreadMethodEntry entry = _threadCallbackList.Dequeue();
                        entry._exception = ex;
                        entry.Complete();
                    }
                }
            }

            processed = waitHandle.WaitOne(1000, false);
        }
    }

    /// <summary>
    ///  The width of this control.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlWidthDescr))]
    public int Width
    {
        get => _width;
        set => SetBounds(_x, _y, value, _height, BoundsSpecified.Width);
    }

    /// <summary>
    ///  The current exStyle of the hWnd
    /// </summary>
    private protected WINDOW_EX_STYLE ExtendedWindowStyle
    {
        get => (WINDOW_EX_STYLE)PInvokeCore.GetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE);
        set => PInvokeCore.SetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE, (nint)value);
    }

    /// <summary>
    ///  The current style of the hWnd
    /// </summary>
    internal WINDOW_STYLE WindowStyle
    {
        get => (WINDOW_STYLE)PInvokeCore.GetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
        set => PInvokeCore.SetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE, (nint)value);
    }

    /// <summary>
    ///  The target of Win32 window messages.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlWindowTargetDescr))]
    public IWindowTarget WindowTarget
    {
        get => _window.WindowTarget;
        set => _window.WindowTarget = value;
    }

    /// <summary>
    ///  The current text of the Window; if the window has not yet been created, stores it in the control.
    ///  If the window has been created, stores the text in the underlying win32 control.
    ///  This property should be used whenever you want to get at the win32 control's text. For all other cases,
    ///  use the Text property - but note that this is overridable, and any of your code that uses it will use
    ///  the overridden version in controls that subclass your own.
    /// </summary>
    internal virtual string WindowText
    {
        get
        {
            if (!IsHandleCreated)
            {
                return _text ?? string.Empty;
            }

            using var scope = MultithreadSafeCallScope.Create();
            return PInvokeCore.GetWindowText(this);
        }
        set
        {
            value ??= string.Empty;

            if (!WindowText.Equals(value))
            {
                if (IsHandleCreated)
                {
                    PInvoke.SetWindowText(this, value);
                }
                else
                {
                    _text = value.Length == 0 ? null : value;
                }
            }
        }
    }

    /// <summary>
    ///  Occurs when the control is clicked.
    /// </summary>
    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ControlOnClickDescr))]
    public event EventHandler? Click
    {
        add => Events.AddHandler(s_clickEvent, value);
        remove => Events.RemoveHandler(s_clickEvent, value);
    }

    /// <summary>
    ///  Occurs when a new control is added.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlOnControlAddedDescr))]
    public event ControlEventHandler? ControlAdded
    {
        add => Events.AddHandler(s_controlAddedEvent, value);
        remove => Events.RemoveHandler(s_controlAddedEvent, value);
    }

    /// <summary>
    ///  Occurs when a control is removed.
    /// </summary>
    [SRCategory(nameof(SR.CatBehavior))]
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlOnControlRemovedDescr))]
    public event ControlEventHandler? ControlRemoved
    {
        add => Events.AddHandler(s_controlRemovedEvent, value);
        remove => Events.RemoveHandler(s_controlRemovedEvent, value);
    }

    /// <summary>
    ///  Occurs when the value of the <see cref="DataContext"/> property changes.
    /// </summary>
    [SRCategory(nameof(SR.CatData))]
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlDataContextChangedDescr))]
    public event EventHandler? DataContextChanged
    {
        add => Events.AddHandler(s_dataContextEvent, value);
        remove => Events.RemoveHandler(s_dataContextEvent, value);
    }

    [SRCategory(nameof(SR.CatDragDrop))]
    [SRDescription(nameof(SR.ControlOnDragDropDescr))]
    public event DragEventHandler? DragDrop
    {
        add => Events.AddHandler(s_dragDropEvent, value);
        remove => Events.RemoveHandler(s_dragDropEvent, value);
    }

    [SRCategory(nameof(SR.CatDragDrop))]
    [SRDescription(nameof(SR.ControlOnDragEnterDescr))]
    public event DragEventHandler? DragEnter
    {
        add => Events.AddHandler(s_dragEnterEvent, value);
        remove => Events.RemoveHandler(s_dragEnterEvent, value);
    }

    [SRCategory(nameof(SR.CatDragDrop))]
    [SRDescription(nameof(SR.ControlOnDragOverDescr))]
    public event DragEventHandler? DragOver
    {
        add => Events.AddHandler(s_dragOverEvent, value);
        remove => Events.RemoveHandler(s_dragOverEvent, value);
    }

    [SRCategory(nameof(SR.CatDragDrop))]
    [SRDescription(nameof(SR.ControlOnDragLeaveDescr))]
    public event EventHandler? DragLeave
    {
        add => Events.AddHandler(s_dragLeaveEvent, value);
        remove => Events.RemoveHandler(s_dragLeaveEvent, value);
    }

    [SRCategory(nameof(SR.CatDragDrop))]
    [SRDescription(nameof(SR.ControlOnGiveFeedbackDescr))]
    public event GiveFeedbackEventHandler? GiveFeedback
    {
        add => Events.AddHandler(s_giveFeedbackEvent, value);
        remove => Events.RemoveHandler(s_giveFeedbackEvent, value);
    }

    /// <summary>
    ///  Occurs when a handle is created for the control.
    /// </summary>
    [SRCategory(nameof(SR.CatPrivate))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlOnCreateHandleDescr))]
    public event EventHandler? HandleCreated
    {
        add => Events.AddHandler(s_handleCreatedEvent, value);
        remove => Events.RemoveHandler(s_handleCreatedEvent, value);
    }

    /// <summary>
    ///  Occurs when the control's handle is destroyed.
    /// </summary>
    [SRCategory(nameof(SR.CatPrivate))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlOnDestroyHandleDescr))]
    public event EventHandler? HandleDestroyed
    {
        add => Events.AddHandler(s_handleDestroyedEvent, value);
        remove => Events.RemoveHandler(s_handleDestroyedEvent, value);
    }

    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ControlOnHelpDescr))]
    public event HelpEventHandler? HelpRequested
    {
        add => Events.AddHandler(s_helpRequestedEvent, value);
        remove => Events.RemoveHandler(s_helpRequestedEvent, value);
    }

    [SRCategory(nameof(SR.CatAppearance))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [SRDescription(nameof(SR.ControlOnInvalidateDescr))]
    public event InvalidateEventHandler? Invalidated
    {
        add => Events.AddHandler(s_invalidatedEvent, value);
        remove => Events.RemoveHandler(s_invalidatedEvent, value);
    }

    [Browsable(false)]
    public Size PreferredSize
    {
        get { return GetPreferredSize(Size.Empty); }
    }

    [SRDescription(nameof(SR.ControlPaddingDescr))]
    [SRCategory(nameof(SR.CatLayout))]
    [Localizable(true)]
    public Padding Padding
    {
        get { return CommonProperties.GetPadding(this, DefaultPadding); }
        set
        {
            if (value != Padding)
            {
                CommonProperties.SetPadding(this, value);
                // Ideally we are being laid out by a LayoutEngine that cares about our preferred size.
                // We set our LAYOUTISDIRTY bit and ask our parent to refresh us.
                SetState(States.LayoutIsDirty, true);
                using (new LayoutTransaction(ParentInternal, this, PropertyNames.Padding))
                {
                    OnPaddingChanged(EventArgs.Empty);
                }

                if (GetState(States.LayoutIsDirty))
                {
                    // The above did not cause our layout to be refreshed. We explicitly refresh our
                    // layout to ensure that any children are repositioned to account for the change
                    // in padding.
                    LayoutTransaction.DoLayout(this, this, PropertyNames.Padding);
                }
            }
        }
    }

    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnPaddingChangedDescr))]
    public event EventHandler? PaddingChanged
    {
        add => Events.AddHandler(s_paddingChangedEvent, value);
        remove => Events.RemoveHandler(s_paddingChangedEvent, value);
    }

    [SRCategory(nameof(SR.CatAppearance))]
    [SRDescription(nameof(SR.ControlOnPaintDescr))]
    public event PaintEventHandler? Paint
    {
        add => Events.AddHandler(s_paintEvent, value);
        remove => Events.RemoveHandler(s_paintEvent, value);
    }

    [SRCategory(nameof(SR.CatDragDrop))]
    [SRDescription(nameof(SR.ControlOnQueryContinueDragDescr))]
    public event QueryContinueDragEventHandler? QueryContinueDrag
    {
        add => Events.AddHandler(s_queryContinueDragEvent, value);
        remove => Events.RemoveHandler(s_queryContinueDragEvent, value);
    }

    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ControlOnQueryAccessibilityHelpDescr))]
    public event QueryAccessibilityHelpEventHandler? QueryAccessibilityHelp
    {
        add => Events.AddHandler(s_queryAccessibilityHelpEvent, value);
        remove => Events.RemoveHandler(s_queryAccessibilityHelpEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is double clicked.
    /// </summary>
    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ControlOnDoubleClickDescr))]
    public event EventHandler? DoubleClick
    {
        add => Events.AddHandler(s_doubleClickEvent, value);
        remove => Events.RemoveHandler(s_doubleClickEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is entered.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlOnEnterDescr))]
    public event EventHandler? Enter
    {
        add => Events.AddHandler(s_enterEvent, value);
        remove => Events.RemoveHandler(s_enterEvent, value);
    }

    /// <summary>
    ///  Occurs when the control receives focus.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlOnGotFocusDescr))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public event EventHandler? GotFocus
    {
        add => Events.AddHandler(s_gotFocusEvent, value);
        remove => Events.RemoveHandler(s_gotFocusEvent, value);
    }

    /// <summary>
    ///  Occurs when a key is pressed down while the control has focus.
    /// </summary>
    [SRCategory(nameof(SR.CatKey))]
    [SRDescription(nameof(SR.ControlOnKeyDownDescr))]
    public event KeyEventHandler? KeyDown
    {
        add => Events.AddHandler(s_keyDownEvent, value);
        remove => Events.RemoveHandler(s_keyDownEvent, value);
    }

    /// <summary>
    ///  Occurs when a key is pressed while the control has focus.
    /// </summary>
    [SRCategory(nameof(SR.CatKey))]
    [SRDescription(nameof(SR.ControlOnKeyPressDescr))]
    public event KeyPressEventHandler? KeyPress
    {
        add => Events.AddHandler(s_keyPressEvent, value);
        remove => Events.RemoveHandler(s_keyPressEvent, value);
    }

    /// <summary>
    ///  Occurs when a key is released while the control has focus.
    /// </summary>
    [SRCategory(nameof(SR.CatKey))]
    [SRDescription(nameof(SR.ControlOnKeyUpDescr))]
    public event KeyEventHandler? KeyUp
    {
        add => Events.AddHandler(s_keyUpEvent, value);
        remove => Events.RemoveHandler(s_keyUpEvent, value);
    }

    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnLayoutDescr))]
    public event LayoutEventHandler? Layout
    {
        add => Events.AddHandler(s_layoutEvent, value);
        remove => Events.RemoveHandler(s_layoutEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is left.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlOnLeaveDescr))]
    public event EventHandler? Leave
    {
        add => Events.AddHandler(s_leaveEvent, value);
        remove => Events.RemoveHandler(s_leaveEvent, value);
    }

    /// <summary>
    ///  Occurs when the control loses focus.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlOnLostFocusDescr))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public event EventHandler? LostFocus
    {
        add => Events.AddHandler(s_lostFocusEvent, value);
        remove => Events.RemoveHandler(s_lostFocusEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is mouse clicked.
    /// </summary>
    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ControlOnMouseClickDescr))]
    public event MouseEventHandler? MouseClick
    {
        add => Events.AddHandler(s_mouseClickEvent, value);
        remove => Events.RemoveHandler(s_mouseClickEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is mouse double clicked.
    /// </summary>
    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ControlOnMouseDoubleClickDescr))]
    public event MouseEventHandler? MouseDoubleClick
    {
        add => Events.AddHandler(s_mouseDoubleClickEvent, value);
        remove => Events.RemoveHandler(s_mouseDoubleClickEvent, value);
    }

    /// <summary>
    ///  Occurs when the control loses mouse Capture.
    /// </summary>
    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ControlOnMouseCaptureChangedDescr))]
    public event EventHandler? MouseCaptureChanged
    {
        add => Events.AddHandler(s_mouseCaptureChangedEvent, value);
        remove => Events.RemoveHandler(s_mouseCaptureChangedEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse pointer is over the control and a mouse button is
    ///  pressed.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseDownDescr))]
    public event MouseEventHandler? MouseDown
    {
        add => Events.AddHandler(s_mouseDownEvent, value);
        remove => Events.RemoveHandler(s_mouseDownEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse pointer enters the control.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseEnterDescr))]
    public event EventHandler? MouseEnter
    {
        add => Events.AddHandler(s_mouseEnterEvent, value);
        remove => Events.RemoveHandler(s_mouseEnterEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse pointer leaves the control.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseLeaveDescr))]
    public event EventHandler? MouseLeave
    {
        add => Events.AddHandler(s_mouseLeaveEvent, value);
        remove => Events.RemoveHandler(s_mouseLeaveEvent, value);
    }

    /// <summary>
    ///  Occurs when the Dpi resolution of the screen this control is displayed on changes,
    ///  either when the top level window is moved between monitors or when the OS settings are changed.
    ///  This event is raised before the top level parent window receives WM_DPICHANGED message.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnDpiChangedBeforeParentDescr))]
    public event EventHandler? DpiChangedBeforeParent
    {
        add => Events.AddHandler(s_dpiChangedBeforeParentEvent, value);
        remove => Events.RemoveHandler(s_dpiChangedBeforeParentEvent, value);
    }

    /// <summary>
    ///  Occurs when the Dpi resolution of the screen this control is displayed on changes,
    ///  either when the top level window is moved between monitors or when the OS settings are changed.
    ///  This message is received after the top level parent window receives WM_DPICHANGED message.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnDpiChangedAfterParentDescr))]
    public event EventHandler? DpiChangedAfterParent
    {
        add => Events.AddHandler(s_dpiChangedAfterParentEvent, value);
        remove => Events.RemoveHandler(s_dpiChangedAfterParentEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse pointer hovers over the control.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseHoverDescr))]
    public event EventHandler? MouseHover
    {
        add => Events.AddHandler(s_mouseHoverEvent, value);
        remove => Events.RemoveHandler(s_mouseHoverEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse pointer is moved over the control.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseMoveDescr))]
    public event MouseEventHandler? MouseMove
    {
        add => Events.AddHandler(s_mouseMoveEvent, value);
        remove => Events.RemoveHandler(s_mouseMoveEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse pointer is over the control and a mouse button is released.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseUpDescr))]
    public event MouseEventHandler? MouseUp
    {
        add => Events.AddHandler(s_mouseUpEvent, value);
        remove => Events.RemoveHandler(s_mouseUpEvent, value);
    }

    /// <summary>
    ///  Occurs when the mouse wheel moves while the control has focus.
    /// </summary>
    [SRCategory(nameof(SR.CatMouse))]
    [SRDescription(nameof(SR.ControlOnMouseWheelDescr))]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public event MouseEventHandler? MouseWheel
    {
        add => Events.AddHandler(s_mouseWheelEvent, value);
        remove => Events.RemoveHandler(s_mouseWheelEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is moved.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnMoveDescr))]
    public event EventHandler? Move
    {
        add => Events.AddHandler(s_moveEvent, value);
        remove => Events.RemoveHandler(s_moveEvent, value);
    }

    /// <summary>
    ///  Raised to preview a key down event
    /// </summary>
    [SRCategory(nameof(SR.CatKey))]
    [SRDescription(nameof(SR.PreviewKeyDownDescr))]
    public event PreviewKeyDownEventHandler? PreviewKeyDown
    {
        add => Events.AddHandler(s_previewKeyDownEvent, value);
        remove => Events.RemoveHandler(s_previewKeyDownEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is resized.
    /// </summary>
    [SRCategory(nameof(SR.CatLayout))]
    [SRDescription(nameof(SR.ControlOnResizeDescr))]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public event EventHandler? Resize
    {
        add => Events.AddHandler(s_resizeEvent, value);
        remove => Events.RemoveHandler(s_resizeEvent, value);
    }

    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ControlOnChangeUICuesDescr))]
    public event UICuesEventHandler? ChangeUICues
    {
        add => Events.AddHandler(s_changeUICuesEvent, value);
        remove => Events.RemoveHandler(s_changeUICuesEvent, value);
    }

    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ControlOnStyleChangedDescr))]
    public event EventHandler? StyleChanged
    {
        add => Events.AddHandler(s_styleChangedEvent, value);
        remove => Events.RemoveHandler(s_styleChangedEvent, value);
    }

    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ControlOnSystemColorsChangedDescr))]
    public event EventHandler? SystemColorsChanged
    {
        add => Events.AddHandler(s_systemColorsChangedEvent, value);
        remove => Events.RemoveHandler(s_systemColorsChangedEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is validating.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlOnValidatingDescr))]
    public event CancelEventHandler? Validating
    {
        add => Events.AddHandler(s_validatingEvent, value);
        remove => Events.RemoveHandler(s_validatingEvent, value);
    }

    /// <summary>
    ///  Occurs when the control is done validating.
    /// </summary>
    [SRCategory(nameof(SR.CatFocus))]
    [SRDescription(nameof(SR.ControlOnValidatedDescr))]
    public event EventHandler? Validated
    {
        add => Events.AddHandler(s_validatedEvent, value);
        remove => Events.RemoveHandler(s_validatedEvent, value);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected internal void AccessibilityNotifyClients(AccessibleEvents accEvent, int childID)
    {
        AccessibilityNotifyClients(accEvent, (int)OBJECT_IDENTIFIER.OBJID_CLIENT, childID);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void AccessibilityNotifyClients(AccessibleEvents accEvent, int objectID, int childID)
    {
        if (IsHandleCreated && !LocalAppContextSwitches.NoClientNotifications)
        {
            PInvoke.NotifyWinEvent((uint)accEvent, this, objectID, childID + 1);
        }
    }

    /// <summary>
    ///  Assigns a new parent control. Sends out the appropriate property change
    ///  notifications for properties that are affected by the change of parent.
    /// </summary>
    internal virtual void AssignParent(Control? value)
    {
        // Adopt the parent's required scaling bits
        if (value is not null)
        {
            RequiredScalingEnabled = value.RequiredScalingEnabled;
        }

        if (CanAccessProperties)
        {
            // Store the old values for these properties
            Font oldFont = Font;
            Color oldForeColor = ForeColor;
            Color oldBackColor = BackColor;
            RightToLeft oldRtl = RightToLeft;
            bool oldEnabled = Enabled;
            bool oldVisible = Visible;

            // Update the parent
            _parent = value;
            OnParentChanged(EventArgs.Empty);

            if (GetAnyDisposingInHierarchy())
            {
                return;
            }

            // Compare property values with new parent to old values
            if (oldEnabled != Enabled)
            {
                OnEnabledChanged(EventArgs.Empty);
            }

            // When a control seems to be going from invisible -> visible,
            // yet its parent is being set to null and it's not top level, do not raise OnVisibleChanged.
            bool newVisible = Visible;

            if (oldVisible != newVisible && !(!oldVisible && newVisible && _parent is null && !GetTopLevel()))
            {
                OnVisibleChanged(EventArgs.Empty);
            }

            if (!oldFont.Equals(Font))
            {
                OnFontChanged(EventArgs.Empty);
            }

            if (!oldForeColor.Equals(ForeColor))
            {
                OnForeColorChanged(EventArgs.Empty);
            }

            if (!oldBackColor.Equals(BackColor))
            {
                OnBackColorChanged(EventArgs.Empty);
            }

            if (oldRtl != RightToLeft)
            {
                OnRightToLeftChanged(EventArgs.Empty);
            }

            if (!Properties.ContainsKey(s_bindingManagerProperty) && Created)
            {
                // We do not want to call our parent's BindingContext property here.
                // We have no idea if us or any of our children are using data binding,
                // and invoking the property would just create the binding manager, which
                // we don't need. We just blindly notify that the binding manager has
                // changed, and if anyone cares, they will do the comparison at that time.
                OnBindingContextChanged(EventArgs.Empty);
            }
        }
        else
        {
            _parent = value;
            OnParentChanged(EventArgs.Empty);
        }

        SetState(States.CheckedHost, false);

        _forceAnchorCalculations = LocalAppContextSwitches.AnchorLayoutV2; // Parent has changed. AnchorsInfo should be recalculated.
        try
        {
            ParentInternal?.LayoutEngine.InitLayout(this, BoundsSpecified.All);
        }
        finally
        {
            _forceAnchorCalculations = false;
        }
    }

    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnParentChangedDescr))]
    public event EventHandler? ParentChanged
    {
        add => Events.AddHandler(s_parentEvent, value);
        remove => Events.RemoveHandler(s_parentEvent, value);
    }

    /// <summary>
    ///  Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on.
    /// </summary>
    /// <param name="method">A delegate to a method that takes no parameters.</param>
    /// <returns>
    ///  An <see cref="IAsyncResult"/> that represents the result of the <see cref="BeginInvoke(Delegate)"/> operation.
    /// </returns>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public IAsyncResult BeginInvoke(Delegate method) => BeginInvoke(method, null);

    /// <summary>
    ///  Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on.
    /// </summary>
    /// <param name="method">A delegate to a method that takes no parameters.</param>
    /// <returns>
    ///  An <see cref="IAsyncResult"/> that represents the result of the <see cref="BeginInvoke(Action)"/> operation.
    /// </returns>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public IAsyncResult BeginInvoke(Action method) => BeginInvoke(method, null);

    /// <summary>
    ///  Executes the given delegate on the thread that owns this Control's
    ///  underlying window handle. The delegate is called asynchronously and this
    ///  method returns immediately. You may call this from any thread, even the
    ///  thread that owns the control's handle. If the control's handle doesn't
    ///  exist yet, this will follow up the control's parent chain until it finds a
    ///  control or form that does have a window handle. If no appropriate handle
    ///  can be found, BeginInvoke will throw an exception. Exceptions within the
    ///  delegate method are considered un-trapped and will be sent to the
    ///  application's un-trapped exception handler.
    ///
    ///  There are five functions on a control that are safe to call from any
    ///  thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
    ///  For all other method calls, you should use one of the invoke methods to marshal
    ///  the call to the control's thread.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public IAsyncResult BeginInvoke(Delegate method, params object?[]? args)
    {
        using var scope = MultithreadSafeCallScope.Create();
        Control marshaler = FindMarshalingControl();
        return (IAsyncResult)marshaler.MarshaledInvoke(this, method, args, synchronous: false);
    }

    internal void BeginUpdateInternal()
    {
        if (!IsHandleCreated)
        {
            return;
        }

        if (_updateCount == 0)
        {
            PInvokeCore.SendMessage(this, PInvokeCore.WM_SETREDRAW, (WPARAM)(BOOL)false);
        }

        _updateCount++;
    }

    /// <summary>
    ///  Brings this control to the front of the Z-order.
    /// </summary>
    public void BringToFront()
    {
        if (_parent is not null)
        {
            _parent.Controls.SetChildIndex(this, 0);
        }
        else if (IsHandleCreated && GetTopLevel() && PInvoke.IsWindowEnabled(this))
        {
            PInvoke.SetWindowPos(
                this,
                HWND.HWND_TOP,
                0, 0, 0, 0,
                SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
        }
    }

    /// <summary>
    ///  Specifies whether this control can process the mnemonic or not. A condition to process a mnemonic is that
    ///  all controls in the parent chain can do it too, but since the semantics for this function can be overridden,
    ///  we need to call the method on the parent 'recursively' (not exactly since it is not necessarily the same method).
    /// </summary>
    internal virtual bool CanProcessMnemonic() =>
        Enabled && Visible && (_parent is null || _parent.CanProcessMnemonic());

    // Package scope to allow AxHost to override
    internal virtual bool CanSelectCore()
    {
        if ((_controlStyle & ControlStyles.Selectable) != ControlStyles.Selectable)
        {
            return false;
        }

        for (Control? ctl = this; ctl is not null; ctl = ctl._parent)
        {
            if (!ctl.Enabled || !ctl.Visible)
            {
                return false;
            }
        }

        return true;
    }

    /// <summary>
    ///  Searches the parent/owner tree for bottom to find any instance
    ///  of toFind in the parent/owner tree.
    /// </summary>
    internal static void CheckParentingCycle(Control? bottom, Control? toFind)
    {
        Form? lastOwner = null;
        Control? lastParent = null;

        for (Control? ctl = bottom; ctl is not null; ctl = ctl.ParentInternal)
        {
            lastParent = ctl;
            if (ctl == toFind)
            {
                throw new ArgumentException(SR.CircularOwner);
            }
        }

        if (lastParent is not null)
        {
            if (lastParent is Form f)
            {
                for (Form? form = f; form is not null; form = form.OwnerInternal)
                {
                    lastOwner = form;
                    if (form == toFind)
                    {
                        throw new ArgumentException(SR.CircularOwner);
                    }
                }
            }
        }

        if (lastOwner is not null)
        {
            if (lastOwner.ParentInternal is not null)
            {
                CheckParentingCycle(lastOwner.ParentInternal, toFind);
            }
        }
    }

    private void ChildGotFocus(Control child)
    {
        if (IsActiveX)
        {
            ActiveXOnFocus(true);
        }

        _parent?.ChildGotFocus(child);
    }

    /// <summary>
    ///  Verifies if a control is a child of this control.
    /// </summary>
    public bool Contains([NotNullWhen(true)] Control? ctl)
    {
        while (ctl is not null)
        {
            ctl = ctl.ParentInternal;
            if (ctl is null)
            {
                return false;
            }

            if (ctl == this)
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    ///  Constructs a new instance of the accessibility object for this control. Subclasses
    ///  should not call base.CreateAccessibilityObject.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual AccessibleObject CreateAccessibilityInstance()
    {
        return new ControlAccessibleObject(this);
    }

    /// <summary>
    ///  Constructs the new instance of the Controls collection objects. Subclasses
    ///  should not call base.CreateControlsInstance.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual ControlCollection CreateControlsInstance()
    {
        return new ControlCollection(this);
    }

    /// <summary>
    ///  Creates a Graphics for this control. The control's brush, font, foreground
    ///  color and background color become the default values for the Graphics.
    ///  The returned Graphics must be disposed through a call to its dispose()
    ///  method when it is no longer needed. The Graphics Object is only valid for
    ///  the duration of the current window's message.
    /// </summary>
    public Graphics CreateGraphics()
    {
        using var scope = MultithreadSafeCallScope.Create();
        return CreateGraphicsInternal();
    }

    internal Graphics CreateGraphicsInternal()
    {
        return Graphics.FromHwndInternal(Handle);
    }

    /// <summary>
    ///  Creates a handle for this control. This method is called by the framework, this should
    ///  not be called directly. Inheriting classes should always call <c>base.CreateHandle()</c> when
    ///  overriding this method.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void CreateHandle()
    {
        ObjectDisposedException.ThrowIf(GetState(States.Disposed), this);

        if (GetState(States.CreatingHandle))
        {
            return;
        }

        Rectangle originalBounds;

        try
        {
            SetState(States.CreatingHandle, true);

            originalBounds = Bounds;

            // Activate theming scope to get theming for controls at design time and when hosted in browser.
            // NOTE: If a theming context is already active, this call is very fast, so shouldn't be a perf issue.
            using ThemingScope scope = new(Application.UseVisualStyles);

            CreateParams cp = CreateParams;
            SetState(States.Mirrored, (cp.ExStyle & (int)WINDOW_EX_STYLE.WS_EX_LAYOUTRTL) != 0);

            // Adjust for scrolling of parent.
            if (_parent is not null)
            {
                Rectangle parentClient = _parent.ClientRectangle;

                if (!parentClient.IsEmpty)
                {
                    if (cp.X != PInvoke.CW_USEDEFAULT)
                    {
                        cp.X -= parentClient.X;
                    }

                    if (cp.Y != PInvoke.CW_USEDEFAULT)
                    {
                        cp.Y -= parentClient.Y;
                    }
                }
            }

            // And if we are WS_CHILD, ensure we have a parent handle.
            if (cp.Parent == IntPtr.Zero && (cp.Style & (int)WINDOW_STYLE.WS_CHILD) != 0)
            {
                Debug.Assert((cp.ExStyle & (int)WINDOW_EX_STYLE.WS_EX_MDICHILD) == 0, "Can't put MDI child forms on the parking form");
                Application.ParkHandle(cp, DpiAwarenessContext);
            }

            _window.CreateHandle(cp);

            UpdateReflectParent();
        }
        finally
        {
            SetState(States.CreatingHandle, false);
        }

        // For certain controls (e.g., ComboBox) CreateWindowEx
        // may cause the control to resize. WM_SETWINDOWPOSCHANGED takes care of
        // the control being resized, but our layout container may need a refresh as well.
        if (Bounds != originalBounds)
        {
            LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds);
        }
    }

    /// <summary>
    ///  Forces the creation of the control. This includes the creation of the handle,
    ///  and any child controls.
    /// </summary>
    public void CreateControl()
    {
        bool controlIsAlreadyCreated = Created;
        CreateControl(ignoreVisible: false);

        if (!Properties.ContainsKey(s_bindingManagerProperty) && ParentInternal is not null && !controlIsAlreadyCreated)
        {
            // We do not want to call our parent's BindingContext property here.
            // We have no idea if us or any of our children are using data binding,
            // and invoking the property would just create the binding manager, which
            // we don't need. We just blindly notify that the binding manager has
            // changed, and if anyone cares, they will do the comparison at that time.
            OnBindingContextChanged(EventArgs.Empty);
        }
    }

    /// <summary>
    ///  Forces the creation of the control if it is visible. This includes the creation of the handle,
    ///  and any child controls.
    /// </summary>
    /// <param name="ignoreVisible">
    ///  When <see langword="true"/> create even if the control is not visible.
    /// </param>
    internal void CreateControl(bool ignoreVisible)
    {
        // Unless specified otherwise, only "create" the control if it is visible for performance. This has the
        // effect of delayed handle creation of hidden controls.
        if (!ignoreVisible && (GetState(States.Created) || !Visible))
        {
            return;
        }

        SetState(States.Created, true);
        bool createdOK = false;
        try
        {
            if (!IsHandleCreated)
            {
                CreateHandle();
            }

            if (ChildControls is { } children)
            {
                // Snapshot this array because z-order updates from Windows may rearrange it.
                Control[] controlSnapshot = new Control[children.Count];
                children.CopyTo(controlSnapshot, 0);

                foreach (Control control in controlSnapshot)
                {
                    if (control.IsHandleCreated)
                    {
                        control.SetParentHandle(HWND);
                    }

                    control.CreateControl(ignoreVisible);
                }
            }

            createdOK = true;
        }
        finally
        {
            if (!createdOK)
            {
                SetState(States.Created, false);
            }
        }

        OnCreateControl();
    }

    /// <summary>
    ///  Sends the specified message to the default window procedure.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void DefWndProc(ref Message m) => _window.DefWndProc(ref m);

    /// <summary>
    ///  Destroys the handle associated with this control. Inheriting classes should
    ///  always call base.destroyHandle.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void DestroyHandle()
    {
        if (RecreatingHandle && _threadCallbackList is not null)
        {
            // See if we have a thread marshaling request pending. If so, we will need to
            // re-post it after recreating the handle.
            lock (_threadCallbackList)
            {
                if (s_threadCallbackMessage != 0)
                {
                    MSG msg = default;
                    BOOL result = PInvokeCore.PeekMessage(
                        &msg,
                        this,
                        (uint)s_threadCallbackMessage,
                        (uint)s_threadCallbackMessage,
                        PEEK_MESSAGE_REMOVE_TYPE.PM_NOREMOVE);

                    if (result)
                    {
                        SetState(States.ThreadMarshalPending, true);
                    }
                }
            }
        }

        // If we're not recreating the handle, then any items in the thread callback list will
        // be orphaned. An orphaned item is bad, because it will cause the thread to never
        // wake up. So, we put exceptions into all these items and wake up all threads.
        // If we are recreating the handle, then we're fine because recreation will re-post
        // the thread callback message to the new handle for us.
        if (!RecreatingHandle && _threadCallbackList is not null)
        {
            lock (_threadCallbackList)
            {
                Exception ex = new ObjectDisposedException(GetType().Name);

                while (_threadCallbackList.Count > 0)
                {
                    ThreadMethodEntry entry = _threadCallbackList.Dequeue();
                    entry._exception = ex;
                    entry.Complete();
                }
            }
        }

        if (((WINDOW_EX_STYLE)PInvokeCore.GetWindowLong(_window, WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE))
            .HasFlag(WINDOW_EX_STYLE.WS_EX_MDICHILD))
        {
            PInvoke.DefMDIChildProc(InternalHandle, PInvokeCore.WM_CLOSE, default, default);
        }
        else
        {
            _window.DestroyHandle();
        }

        _trackMouseEvent = default;
    }

    /// <summary>
    ///  Disposes of the resources (other than memory) used by the <see cref="Control"/>.
    /// </summary>
    protected override void Dispose(bool disposing)
    {
        if (GetState(States.OwnCtlBrush))
        {
            if (Properties.TryGetValue(s_backBrushProperty, out HBRUSH backBrush))
            {
                if (!backBrush.IsNull)
                {
                    PInvokeCore.DeleteObject(backBrush);
                }

                Properties.RemoveValue(s_backBrushProperty);
            }
        }

        // Unsubscribes from the Disposed event of the ContextMenuStrip.
        if (ContextMenuStrip is ContextMenuStrip menu)
        {
            menu.Disposed -= DetachContextMenuStrip;
        }

        ReflectParent = null;

        if (disposing)
        {
            if (GetState(States.Disposing))
            {
                return;
            }

            if (GetState(States.CreatingHandle))
            {
                throw new InvalidOperationException(string.Format(SR.ClosingWhileCreatingHandle, "Dispose"));
                // I imagine most subclasses will get themselves in a half disposed state
                // if this exception is thrown, but things will be equally broken if we ignore this error,
                // and this way at least the user knows what they did wrong.
            }

            SetState(States.Disposing, true);
            SuspendLayout();
            try
            {
                Properties.RemoveValue(s_ncAccessibilityProperty);

                DisposeAxControls();
                Properties.GetValueOrDefault<ActiveXImpl>(s_activeXImplProperty)?.Dispose();

                ResetBindings();

                if (IsHandleCreated)
                {
                    DestroyHandle();
                }

                _parent?.Controls.Remove(this);

                if (ChildControls is { } children)
                {
                    for (int i = 0; i < children.Count; i++)
                    {
                        Control child = children[i];
                        child._parent = null;
                        child.Dispose();
                    }

                    ChildControls = null;
                }

                ClearDpiFonts();
                base.Dispose(disposing);
            }
            finally
            {
                ResumeLayout(false);
                SetState(States.Disposing, false);
                SetState(States.Disposed, true);
            }
        }
        else
        {
            // This same post is done in NativeWindow's finalize method, so if you change
            // it, change it there too.
            _window?.ForceExitMessageLoop();

            base.Dispose(disposing);
        }
    }

    // Package scope to allow AxHost to override.
    internal virtual void DisposeAxControls()
    {
        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].DisposeAxControls();
            }
        }
    }

    /// <inheritdoc cref="DoDragDropAsJson{T}(T, DragDropEffects, Bitmap?, Point, bool)"/>
    public DragDropEffects DoDragDropAsJson<T>(T data, DragDropEffects allowedEffects) =>
        DoDragDropAsJson(data, allowedEffects, dragImage: null, cursorOffset: default, useDefaultDragImage: false);

    /// <summary>
    ///  Begins a drag operation.
    /// </summary>
    /// <param name="data">The data being dragged.</param>
    /// <param name="allowedEffects">determine which drag operations can occur.</param>
    /// <param name="dragImage">The drag image bitmap.</param>
    /// <param name="cursorOffset">The drag image cursor offset.</param>
    /// <param name="useDefaultDragImage">Indicating whether a layered window drag image is used.</param>
    /// <returns>A value from the <see cref="DragDropEffects"/> enumeration that represents the final effect that was performed during the drag-and-drop operation.</returns>
    /// <exception cref="InvalidOperationException">
    ///  If <paramref name="data"/> is a non derived <see cref="DataObject"/>. This is for better error reporting as <see cref="DataObject"/> will serialize empty. If <see cref="DataObject"/>
    ///  needs to be used to start a drag operation, use <see cref="DataObject.SetDataAsJson{T}(T)"/> to JSON serialize the data being held within the <paramref name="data"/>,
    ///  then pass the <paramref name="data"/> to <see cref="DoDragDrop(object, DragDropEffects)"/>.
    /// </exception>
    /// <remarks>
    ///  <para>
    ///  The data will be stored as JSON if the data is not an intrinsically handled type. Otherwise, it will be stored
    ///  the same as <see cref="DoDragDrop(object, DragDropEffects)"/>.
    ///  </para>
    /// </remarks>
    public DragDropEffects DoDragDropAsJson<T>(
        T data,
        DragDropEffects allowedEffects,
        Bitmap? dragImage,
        Point cursorOffset,
        bool useDefaultDragImage)
    {
        data.OrThrowIfNull(nameof(data));
        if (typeof(T) == typeof(DataObject))
        {
            throw new InvalidOperationException(string.Format(SR.ClipboardOrDragDrop_CannotJsonSerializeDataObject, nameof(DoDragDrop)));
        }

        DataObject dataObject = new();
        dataObject.SetDataAsJson(data);
        return DoDragDrop(dataObject, allowedEffects, dragImage, cursorOffset, useDefaultDragImage);
    }

    /// <summary>
    ///  Begins a drag operation. The allowedEffects determine which
    ///  drag operations can occur. If the drag operation needs to interop
    ///  with applications in another process, data should either be
    ///  a base managed class (String, Bitmap, or Metafile) or some Object
    ///  that implements System.Runtime.Serialization.ISerializable. data can also be any Object that
    ///  implements System.Windows.Forms.IDataObject.
    /// </summary>
    public DragDropEffects DoDragDrop(object data, DragDropEffects allowedEffects) =>
        DoDragDrop(data, allowedEffects, dragImage: null, cursorOffset: default, useDefaultDragImage: false);

    /// <summary>
    ///  Begins a drag operation. The <paramref name="allowedEffects"/> determine which drag operations can occur.
    ///  If the drag operation needs to interop with applications in another process, <paramref name="data"/>
    ///  should either be a base managed class (<see cref="string"/>, <see cref="Bitmap"/>,
    ///  or <see cref="Drawing.Imaging.Metafile"/>) or some <see cref="object"/> that implements
    ///  <see cref="Runtime.Serialization.ISerializable"/>. <paramref name="data"/> can also be any
    ///  <see cref="object"/> that implements <see cref="IDataObject"/>. <paramref name="dragImage"/> is the bitmap
    ///  that will be displayed during the  drag operation and <paramref name="cursorOffset"/> specifies the location
    ///  of the cursor within <paramref name="dragImage"/>, which is an offset from the upper-left corner.
    ///  Specify <see langword="true"/> for <paramref name="useDefaultDragImage"/> to use a layered window drag image
    ///  with a size of 96x96; otherwise <see langword="false"/>. Note the outer edges of <paramref name="dragImage"/>
    ///  are blended out if the image width or height exceeds 300 pixels.
    /// </summary>
    /// <returns>
    ///  A value from the <see cref="DragDropEffects"/> enumeration that represents the final effect that was performed
    ///  during the drag-and-drop operation.
    /// </returns>
    /// <remarks>
    ///  <para>
    ///   Because <see cref="DoDragDrop(object, DragDropEffects, Bitmap, Point, bool)"/> always performs the
    ///   RGB multiplication step in calculating the alpha value, you should always pass a <see cref="Bitmap"/>
    ///   without pre-multiplied alpha blending. Note that no error will result from passing a <see cref="Bitmap"/>
    ///   with pre-multiplied alpha blending, but this method will multiply it again,
    ///   doubling the resulting alpha value.
    ///  </para>
    /// </remarks>
    public DragDropEffects DoDragDrop(
        object data,
        DragDropEffects allowedEffects,
        Bitmap? dragImage,
        Point cursorOffset,
        bool useDefaultDragImage)
    {
        ComTypes.IDataObject dataObject = CreateRuntimeDataObjectForDrag(data);

        DROPEFFECT finalEffect;

        try
        {
            using var dropSource = ComHelpers.GetComScope<IDropSource>(
                new DropSource(this, dataObject, dragImage, cursorOffset, useDefaultDragImage));
            using var dataScope = ComHelpers.GetComScope<Com.IDataObject>(dataObject);
            if (PInvoke.DoDragDrop(dataScope, dropSource, (DROPEFFECT)(uint)allowedEffects, out finalEffect).Failed)
            {
                return DragDropEffects.None;
            }
        }
        finally
        {
            if (DragDropHelper.IsInDragLoop(dataObject))
            {
                DragDropHelper.SetInDragLoop(dataObject, inDragLoop: false);
            }
        }

        return (DragDropEffects)finalEffect;
    }

    /// <summary>
    ///  Creates <see cref="DataObject"/> for drag operation.
    ///  The incoming <paramref name="data"/> will always be wrapped.
    /// </summary>
    private static DataObject CreateRuntimeDataObjectForDrag(object data) =>
        data is DataObject dataObject ? dataObject : new DataObject(data);

    public void DrawToBitmap(Bitmap bitmap, Rectangle targetBounds)
    {
        ArgumentNullException.ThrowIfNull(bitmap);

        if (targetBounds.Width <= 0 || targetBounds.Height <= 0
            || targetBounds.X < 0 || targetBounds.Y < 0)
        {
            throw new ArgumentException(message: null, nameof(targetBounds));
        }

        if (!IsHandleCreated)
        {
            CreateHandle();
        }

        int width = Math.Min(Width, targetBounds.Width);
        int height = Math.Min(Height, targetBounds.Height);

        using Bitmap image = new(width, height, bitmap.PixelFormat);
        using Graphics g = Graphics.FromImage(image);
        using DeviceContextHdcScope hDc = new(g, applyGraphicsState: false);

        // Send the WM_PRINT message.
        PInvokeCore.SendMessage(
            this,
            PInvokeCore.WM_PRINT,
            (WPARAM)hDc,
            (LPARAM)(PInvoke.PRF_CHILDREN | PInvoke.PRF_CLIENT | PInvoke.PRF_ERASEBKGND | PInvoke.PRF_NONCLIENT));

        // Now BLT the result to the destination bitmap.
        using Graphics destGraphics = Graphics.FromImage(bitmap);
        using DeviceContextHdcScope desthDC = new(destGraphics, applyGraphicsState: false);
        PInvokeCore.BitBlt(
            desthDC,
            targetBounds.X,
            targetBounds.Y,
            width,
            height,
            hDc,
            0,
            0,
            ROP_CODE.SRCCOPY);
    }

    /// <summary>
    ///  Retrieves the return value of the asynchronous operation
    ///  represented by the IAsyncResult interface passed. If the
    ///  async operation has not been completed, this function will
    ///  block until the result is available.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public object? EndInvoke(IAsyncResult asyncResult)
    {
        using var scope = MultithreadSafeCallScope.Create();
        ArgumentNullException.ThrowIfNull(asyncResult);

        if (asyncResult is not ThreadMethodEntry entry)
        {
            throw new ArgumentException(SR.ControlBadAsyncResult, nameof(asyncResult));
        }

        Debug.Assert(this == entry._caller, "Called BeginInvoke on one control, and the corresponding EndInvoke on a different control");

        if (!asyncResult.IsCompleted)
        {
            Control marshaler = FindMarshalingControl();
            if (PInvoke.GetWindowThreadProcessId(marshaler, out _) == PInvokeCore.GetCurrentThreadId())
            {
                marshaler.InvokeMarshaledCallbacks();
            }
            else
            {
                marshaler = entry._marshaler;
                marshaler.WaitForWaitHandle(asyncResult.AsyncWaitHandle);
            }
        }

        Debug.Assert(asyncResult.IsCompleted, "Why isn't this asyncResult done yet?");
        return entry._exception is not null ? throw entry._exception : entry._retVal;
    }

    internal bool EndUpdateInternal() => EndUpdateInternal(invalidate: true);

    internal bool EndUpdateInternal(bool invalidate)
    {
        if (_updateCount > 0)
        {
            Debug.Assert(IsHandleCreated, "Handle should be created by now");
            _updateCount--;
            if (_updateCount == 0)
            {
                PInvokeCore.SendMessage(this, PInvokeCore.WM_SETREDRAW, (WPARAM)(BOOL)true);
                if (invalidate)
                {
                    Invalidate();
                }
            }

            return true;
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    ///  Retrieves the form that this control is on. The control's parent may not be the same as the form.
    /// </summary>
    public Form? FindForm()
    {
        Control? current = this;
        while (current is not null and not Form)
        {
            current = current.ParentInternal;
        }

        return (Form?)current;
    }

    /// <summary>
    ///  Attempts to find a control Object that we can use to marshal
    ///  calls. We must marshal calls to a control with a window
    ///  handle, so we traverse up the parent chain until we find one.
    ///  Failing that, we just return ourselves.
    /// </summary>
    private Control FindMarshalingControl()
    {
        lock (this)
        {
            Control? c = this;

            while (c is not null && !c.IsHandleCreated)
            {
                Control? p = c.ParentInternal;
                c = p;
            }

            if (c is null)
            {
                // No control with a created handle. We
                // just use our own control. MarshaledInvoke
                // will throw an exception because there
                // is no handle.
                c = this;
            }
            else
            {
                Debug.Assert(c.IsHandleCreated, "FindMarshalingControl chose a bad control.");
            }

            return c;
        }
    }

    protected bool GetTopLevel() => (_state & States.TopLevel) != 0;

    /// <summary>
    ///  Used by AxHost to fire the CreateHandle event.
    /// </summary>
    internal void RaiseCreateHandleEvent(EventArgs e)
    {
        ((EventHandler?)Events[s_handleCreatedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the event associated with key with the event data of
    ///  e and a sender of this control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void RaiseKeyEvent(object key, KeyEventArgs e)
    {
        ((KeyEventHandler?)Events[key])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the event associated with key with the event data of
    ///  e and a sender of this control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void RaiseMouseEvent(object key, MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[key])?.Invoke(this, e);
    }

    /// <summary>
    ///  Attempts to set focus to this control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public bool Focus()
    {
        // Call the internal method (which form overrides)
        return FocusInternal();
    }

    /// <summary>
    ///  Internal method for setting focus to the control.
    ///  Form overrides this method - because MDI child forms
    ///  need to be focused by calling the MDIACTIVATE message.
    /// </summary>
    private protected virtual bool FocusInternal()
    {
        if (CanFocus)
        {
            PInvoke.SetFocus(this);
        }

        if (Focused && ParentInternal is not null)
        {
            IContainerControl? control = ParentInternal.GetContainerControl();

            if (control is not null)
            {
                if (control is ContainerControl containerControl)
                {
                    containerControl.SetActiveControl(this);
                }
                else
                {
                    control.ActiveControl = this;
                }
            }
        }

        return Focused;
    }

    /// <summary>
    ///  Returns the control that is currently associated with handle.
    ///  This method will search up the HWND parent chain until it finds some
    ///  handle that is associated with with a control. This method is more
    ///  robust that fromHandle because it will correctly return controls
    ///  that own more than one handle.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public static Control? FromChildHandle(IntPtr handle)
    {
        HWND hwnd = (HWND)handle;
        while (!hwnd.IsNull)
        {
            Control? control = FromHandle(hwnd);
            if (control is not null)
            {
                return control;
            }

            hwnd = PInvoke.GetAncestor(hwnd, GET_ANCESTOR_FLAGS.GA_PARENT);
        }

        return null;
    }

    /// <summary>
    ///  Creates a <see cref="HandleRef{THandle}"/> for the given <paramref name="hwnd"/>, associating
    ///  it with the first parent <see cref="Control"/> if possible.
    /// </summary>
    internal static HandleRef<HWND> GetHandleRef(HWND hwnd) => new(FromChildHandle(hwnd), hwnd);

    /// <summary>
    ///  Returns the control that is currently associated with handle.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public static Control? FromHandle(IntPtr handle)
    {
        NativeWindow? nativeWindow = NativeWindow.FromHandle(handle);
        while (nativeWindow is not null and not ControlNativeWindow)
        {
            nativeWindow = nativeWindow.PreviousWindow;
        }

        return nativeWindow is ControlNativeWindow controlNativeWindow ? controlNativeWindow.GetControl() : null;
    }

    // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose
    // constraints on their size.
    internal Size ApplySizeConstraints(int width, int height)
    {
        return ApplyBoundsConstraints(0, 0, width, height).Size;
    }

    // GetPreferredSize and SetBoundsCore call this method to allow controls to self impose
    // constraints on their size.
    internal Size ApplySizeConstraints(Size proposedSize)
    {
        return ApplyBoundsConstraints(0, 0, proposedSize.Width, proposedSize.Height).Size;
    }

    internal virtual Rectangle ApplyBoundsConstraints(int suggestedX, int suggestedY, int proposedWidth, int proposedHeight)
    {
        // COMPAT: in Everett we would allow you to set negative values in pre-handle mode
        // in Whidbey, if you've set Min/Max size we will constrain you to 0,0. Everett apps didnt
        // have min/max size on control, which is why this works.
        if (MaximumSize != Size.Empty || MinimumSize != Size.Empty)
        {
            Size maximumSize = LayoutUtils.ConvertZeroToUnbounded(MaximumSize);
            Rectangle newBounds = new(suggestedX, suggestedY, 0, 0)
            {
                // Clip the size to maximum and inflate it to minimum as necessary.
                Size = LayoutUtils.IntersectSizes(new Size(proposedWidth, proposedHeight), maximumSize)
            };
            newBounds.Size = LayoutUtils.UnionSizes(newBounds.Size, MinimumSize);

            return newBounds;
        }

        return new Rectangle(suggestedX, suggestedY, proposedWidth, proposedHeight);
    }

    /// <summary>
    ///  Retrieves the child control that is located at the specified client coordinates.
    /// </summary>
    public Control? GetChildAtPoint(Point pt, GetChildAtPointSkip skipValue)
    {
        int value = (int)skipValue;

        // Since this is a flags enumeration the only way to validate skipValue is by checking if its within the range.
        if (value is < 0 or > 7)
        {
            throw new InvalidEnumArgumentException(nameof(skipValue), value, typeof(GetChildAtPointSkip));
        }

        HWND hwnd = PInvoke.ChildWindowFromPointEx(this, pt, (CWP_FLAGS)value);
        Control? control = FromChildHandle(hwnd);

        return (control == this) ? null : control;
    }

    private protected virtual string? GetCaptionForTool(ToolTip toolTip) =>
        ToolStripControlHost is IKeyboardToolTip host
            ? host.GetCaptionForTool(toolTip)
            : toolTip.GetCaptionForTool(this);

    /// <summary>
    ///  Retrieves the child control that is located at the specified client coordinates.
    /// </summary>
    public Control? GetChildAtPoint(Point pt) => GetChildAtPoint(pt, GetChildAtPointSkip.None);

    /// <summary>
    ///  Returns the closest ContainerControl in the control's chain of parent controls and forms.
    /// </summary>
    public IContainerControl? GetContainerControl()
    {
        // Refer to IsContainerControl property for more details.
        Control? c = IsContainerControl ? ParentInternal : this;

        while (c is not null && !IsFocusManagingContainerControl(c))
        {
            c = c.ParentInternal;
        }

        return (IContainerControl?)c;
    }

    private static bool IsFocusManagingContainerControl(Control ctl)
    {
        return ((ctl._controlStyle & ControlStyles.ContainerControl) == ControlStyles.ContainerControl && ctl is IContainerControl);
    }

    /// <summary>
    ///  This new Internal method checks the updateCount to signify that the control is within the "BeginUpdate"
    ///  and "EndUpdate" cycle. Check out : for usage of this. The TreeView tries to ForceUpdate the scrollbars
    ///  by calling "WM_SETREDRAW" even if the control in "Begin - End" update cycle. Using this Function we can guard
    ///  against repetitively redrawing the control.
    /// </summary>
    internal bool IsUpdating() => _updateCount > 0;

    /// <summary>
    ///  This is a helper method that is called by ScaleControl to retrieve the bounds
    ///  that the control should be scaled by. You may override this method if you
    ///  wish to reuse ScaleControl's scaling logic but you need to supply your own
    ///  bounds. The default implementation returns scaled bounds that take into
    ///  account the BoundsSpecified, whether the control is top level, and whether
    ///  the control is fixed width or auto size, and any adornments the control may have.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified)
    {
        float dx = factor.Width;
        float dy = factor.Height;

        int sx = bounds.X;
        int sy = bounds.Y;

        // Don't reposition top level controls. Also, if we're in
        // design mode, don't reposition the root component.

        if (!GetState(States.TopLevel) && !(
            Site is { } site
            && site.DesignMode
            && site.TryGetService(out IDesignerHost? host)
            && host.RootComponent == this))
        {
            if ((specified & BoundsSpecified.X) != 0)
            {
                sx = (int)Math.Round(bounds.X * dx);
            }

            if ((specified & BoundsSpecified.Y) != 0)
            {
                sy = (int)Math.Round(bounds.Y * dy);
            }
        }

        int sw = bounds.Width;
        int sh = bounds.Height;

        // We should not include the window adornments in our calculation,
        // because windows scales them for us.
        RECT adornmentsBeforeDpiChange = default;
        RECT adornmentsAfterDpiChange = default;
        CreateParams cp = CreateParams;

        // 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())
        {
            AdjustWindowRectExForDpi(ref adornmentsBeforeDpiChange, (WINDOW_STYLE)cp.Style, bMenu: false, (WINDOW_EX_STYLE)cp.ExStyle, _oldDeviceDpi);
        }
        else
        {
            adornmentsBeforeDpiChange = adornmentsAfterDpiChange;
        }

        // Do this even for auto sized controls. They'll "snap back", but it is important to size them in case
        // they are anchored.
        if ((_controlStyle & ControlStyles.FixedWidth) != ControlStyles.FixedWidth && (specified & BoundsSpecified.Width) != 0)
        {
            int localWidth = bounds.Width - adornmentsBeforeDpiChange.Width;
            sw = (int)Math.Round(localWidth * dx) + adornmentsAfterDpiChange.Width;
        }

        if ((_controlStyle & ControlStyles.FixedHeight) != ControlStyles.FixedHeight && (specified & BoundsSpecified.Height) != 0)
        {
            int localHeight = bounds.Height - adornmentsBeforeDpiChange.Height;
            sh = (int)Math.Round(localHeight * dy) + adornmentsAfterDpiChange.Height;
        }

        return new Rectangle(sx, sy, sw, sh);
    }

    private static MouseButtons GetXButton(int wparam) => wparam switch
    {
        PInvoke.XBUTTON1 => MouseButtons.XButton1,
        PInvoke.XBUTTON2 => MouseButtons.XButton2,
        _ => MouseButtons.None,
    };

    internal bool DesiredVisibility => GetState(States.Visible);

    internal bool GetAnyDisposingInHierarchy()
    {
        Control? control = this;
        while (control is not null)
        {
            if (control.Disposing)
            {
                return true;
            }

            control = control.ParentInternal;
        }

        return false;
    }

    /// <summary>
    ///  Returns native child windows sorted according to their TabIndex property order. Controls with the same
    ///  TabIndex remain in original relative child index order (= z-order). Child windows with no corresponding
    ///  Control objects (and therefore no discernable TabIndex) are sorted to the front of the list (but remain
    ///  in relative z-order to one another).
    ///
    ///  This version returns a sorted array of integers, representing the original z-order based indexes of the
    ///  native child windows.
    /// </summary>
    private int[] GetChildWindowsInTabOrder()
    {
        List<ControlTabOrderHolder> holders = [];

        for (HWND hWndChild = PInvoke.GetWindow(this, GET_WINDOW_CMD.GW_CHILD);
            !hWndChild.IsNull;
            hWndChild = PInvoke.GetWindow(new HandleRef<HWND>(this, hWndChild), GET_WINDOW_CMD.GW_HWNDNEXT))
        {
            Control? ctl = FromHandle(hWndChild);
            int tabIndex = (ctl is null) ? -1 : ctl.TabIndex;
            holders.Add(new ControlTabOrderHolder(holders.Count, tabIndex, ctl));
        }

        holders.Sort(ControlTabOrderComparer.Instance);

        int[] indexes = new int[holders.Count];
        for (int i = 0; i < holders.Count; i++)
        {
            indexes[i] = holders[i].OriginalIndex;
        }

        return indexes;
    }

    /// <summary>
    ///  Returns child controls sorted according to their TabIndex property order. Controls with the same TabIndex
    ///  remain in original relative child index order (= z-order).
    ///
    ///  This version returns a sorted array of control references.
    /// </summary>
    internal Control[] GetChildControlsInTabOrder(bool handleCreatedOnly)
    {
        List<ControlTabOrderHolder> holders = new(Controls.Count);

        foreach (Control c in Controls)
        {
            if (!handleCreatedOnly || c.IsHandleCreated)
            {
                holders.Add(new ControlTabOrderHolder(holders.Count, c.TabIndex, c));
            }
        }

        holders.Sort(ControlTabOrderComparer.Instance);

        Control[] controls = new Control[holders.Count];
        for (int i = 0; i < holders.Count; i++)
        {
            controls[i] = holders[i].Control!;
        }

        return controls;
    }

    internal virtual Control? GetFirstChildControlInTabOrder(bool forward)
    {
        if (ChildControls is not { } children)
        {
            return null;
        }

        Control? found = null;
        if (forward)
        {
            for (int c = 0; c < children.Count; c++)
            {
                if (found is null || found._tabIndex > children[c]._tabIndex)
                {
                    found = children[c];
                }
            }
        }
        else
        {
            // Cycle through the controls in reverse z-order looking for the one with the highest
            // tab index.
            for (int c = children.Count - 1; c >= 0; c--)
            {
                if (found is null || found._tabIndex < children[c]._tabIndex)
                {
                    found = children[c];
                }
            }
        }

        return found;
    }

    /// <summary>
    ///  Gets the control <see cref="Font"/>. If the font is inherited, traverse through the parent hierarchy and
    ///  retrieve the font.
    /// </summary>
    /// <param name="fontDpi">Dpi of the control for which <see cref="Font"/> is evaluated.</param>
    /// <returns>The control's <see cref="Font"/></returns>
    private Font GetCurrentFontAndDpi(out int fontDpi)
    {
        fontDpi = _deviceDpi;

        // If application is in PerMonitorV2 mode and font is scaled when moved between monitors.
        if (ScaledControlFont is not null)
        {
            return ScaledControlFont;
        }

        if (TryGetExplicitlySetFont(out Font? font))
        {
            return font;
        }

        if (ParentInternal is { } parent && parent.CanAccessProperties)
        {
            font = parent.GetCurrentFontAndDpi(out fontDpi);
        }

        if (font is not null)
        {
            return font;
        }

        if (IsActiveX)
        {
            font = ActiveXAmbientFont;
            if (font is not null)
            {
                return font;
            }
        }

        return AmbientPropertiesService?.Font ?? DefaultFont;
    }

    private protected virtual IList<Rectangle> GetNeighboringToolsRectangles()
        => ((IKeyboardToolTip?)ToolStripControlHost)?.GetNeighboringToolsRectangles() ?? GetOwnNeighboringToolsRectangles();

    /// <summary>
    ///  Retrieves the next control in the tab order of child controls.
    /// </summary>
    public Control? GetNextControl(Control? ctl, bool forward)
    {
        if (!Contains(ctl))
        {
            ctl = this;
        }

        if (forward)
        {
            if (ChildControls is { } children && children.Count > 0 && (ctl == this || !IsFocusManagingContainerControl(ctl)))
            {
                Control? found = ctl.GetFirstChildControlInTabOrder(forward: true);
                if (found is not null)
                {
                    return found;
                }
            }

            while (ctl != this)
            {
                int targetIndex = ctl!._tabIndex;
                bool hitCtl = false;
                Control? found = null;
                Control? p = ctl._parent;

                // Cycle through the controls in z-order looking for the one with the next highest
                // tab index. Because there can be dups, we have to start with the existing tab index and
                // remember to exclude the current control.
                int parentChildCount = 0;

                ControlCollection? parentChildren = p?.ChildControls;

                if (parentChildren is not null)
                {
                    parentChildCount = parentChildren.Count;
                }

                for (int c = 0; c < parentChildCount; c++)
                {
                    // The logic for this is a bit lengthy, so I have broken it into separate
                    // clauses:

                    // We are not interested in ourself.
                    if (parentChildren![c] != ctl)
                    {
                        // We are interested in controls with >= tab indexes to ctl. We must include those
                        // controls with equal indexes to account for duplicate indexes.
                        if (parentChildren[c]._tabIndex >= targetIndex)
                        {
                            // Check to see if this control replaces the "best match" we've already
                            // found.
                            if (found is null || found._tabIndex > parentChildren[c]._tabIndex)
                            {
                                // Finally, check to make sure that if this tab index is the same as ctl,
                                // that we've already encountered ctl in the z-order. If it isn't the same,
                                // than we're more than happy with it.
                                if (parentChildren[c]._tabIndex != targetIndex || hitCtl)
                                {
                                    found = parentChildren[c];
                                }
                            }
                        }
                    }
                    else
                    {
                        // We track when we have encountered "ctl". We never want to select ctl again, but
                        // we want to know when we've seen it in case we find another control with the same tab index.
                        hitCtl = true;
                    }
                }

                if (found is not null)
                {
                    return found;
                }

                ctl = ctl._parent;
            }
        }
        else
        {
            if (ctl != this)
            {
                int targetIndex = ctl._tabIndex;
                bool hitCtl = false;
                Control? found = null;
                Control? parent = ctl._parent ?? throw new InvalidOperationException(
                    string.Format(SR.ParentPropertyNotSetInGetNextControl, nameof(Parent), ctl));

                ControlCollection? siblings = parent.ChildControls ?? throw new InvalidOperationException(
                    string.Format(SR.ControlsPropertyNotSetInGetNextControl, nameof(Controls), parent));

                int siblingCount = siblings.Count;

                if (siblingCount == 0)
                {
                    throw new InvalidOperationException(string.Format(
                        SR.ControlsCollectionShouldNotBeEmptyInGetNextControl,
                        nameof(Controls),
                        parent));
                }

                // Cycle through the controls in reverse z-order looking for the next lowest tab index. We must
                // start with the same tab index as ctl, because there can be dups.
                for (int c = siblingCount - 1; c >= 0; c--)
                {
                    Control sibling = siblings[c];
                    // The logic for this is a bit lengthy, so I have broken it into separate
                    // clauses:

                    // We are not interested in ourself.
                    if (sibling != ctl)
                    {
                        // We are interested in controls with <= tab indexes to ctl. We must include those
                        // controls with equal indexes to account for duplicate indexes.
                        if (sibling._tabIndex <= targetIndex)
                        {
                            // Check to see if this control replaces the "best match" we've already
                            // found.
                            if (found is null || found._tabIndex < sibling._tabIndex)
                            {
                                // Finally, check to make sure that if this tab index is the same as ctl,
                                // that we've already encountered ctl in the z-order. If it isn't the same,
                                // than we're more than happy with it.
                                if (sibling._tabIndex != targetIndex || hitCtl)
                                {
                                    found = sibling;
                                }
                            }
                        }
                    }
                    else
                    {
                        // We track when we have encountered "ctl". We never want to select ctl again, but
                        // we want to know when we've seen it in case we find another control with the same tab index.
                        hitCtl = true;
                    }
                }

                // If we were unable to find a control we should return the control's parent. However, if that parent is us, return
                // NULL.
                if (found is not null)
                {
                    ctl = found;
                }
                else
                {
                    if (parent == this)
                    {
                        return null;
                    }
                    else
                    {
                        // If we haven't found any siblings, and the control is a ToolStripItem that hosts a control itself,
                        // then we shouldn't return its parent, because it would be the same ToolStrip we're currently at.
                        // Instead, we should return the control that is previous to the current ToolStrip.
                        return ctl.ToolStripControlHost is not null ? GetNextControl(ctl._parent, forward: false) : parent;
                    }
                }
            }

            // We found a control. Walk into this control to find the proper child control within it to select.
            ControlCollection? children = ctl.ChildControls;

            while (children is not null && children.Count > 0 && (ctl == this || !IsFocusManagingContainerControl(ctl)))
            {
                Control? found = ctl.GetFirstChildControlInTabOrder(forward: false);
                if (found is not null)
                {
                    ctl = found;
                    children = ctl.ChildControls;
                }
                else
                {
                    break;
                }
            }
        }

        return ctl == this ? null : ctl;
    }

    /// <summary>
    ///  Return <see cref="Handle"/> if <paramref name="window"/> is a <see cref="Control"/>.
    ///  Otherwise, returns <see cref="IWin32Window.Handle"/> after validating the handle is valid.
    /// </summary>
    internal static HandleRef<HWND> GetSafeHandle(IWin32Window window)
    {
        Debug.Assert(window is not null, "window is null in Control.GetSafeHandle");
        if (window is Control control)
        {
            return new(control);
        }
        else
        {
            HWND hwnd = (HWND)window.Handle;
            return hwnd.IsNull || PInvoke.IsWindow(hwnd)
                ? new(window, hwnd)
                : throw new Win32Exception((int)WIN32_ERROR.ERROR_INVALID_HANDLE);
        }
    }

    /// <summary>
    ///  Retrieves the current value of the specified bit in the control's state.
    /// </summary>
    private protected bool GetState(States flag) => (_state & flag) != 0;

    /// <summary>
    ///  Retrieves the current value of the specified bit in the control's state2.
    /// </summary>
    private protected bool GetExtendedState(ExtendedStates flag) => (_extendedState & flag) != 0;

    /// <summary>
    ///  Retrieves the current value of the specified bit in the control's style.
    ///  This is control style, not the Win32 style of the hWnd.
    /// </summary>
    protected bool GetStyle(ControlStyles flag) => (_controlStyle & flag) == flag;

    /// <summary>
    ///  Hides the control by setting the visible property to false.
    /// </summary>
    public void Hide()
    {
        Visible = false;
    }

    /// <summary>
    ///  Sets up the TrackMouseEvent for listening for the mouse leave event.
    /// </summary>
    private void HookMouseEvent()
    {
        if (!GetState(States.TrackingMouseEvent))
        {
            SetState(States.TrackingMouseEvent, true);

            _trackMouseEvent = new()
            {
                cbSize = (uint)sizeof(TRACKMOUSEEVENT),
                dwFlags = TRACKMOUSEEVENT_FLAGS.TME_LEAVE | TRACKMOUSEEVENT_FLAGS.TME_HOVER,
                hwndTrack = HWND,
                dwHoverTime = 100
            };

            PInvoke.TrackMouseEvent(ref _trackMouseEvent);
        }
    }

    /// <summary>
    ///  Called after the control has been added to another container.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void InitLayout()
    {
        LayoutEngine.InitLayout(this, BoundsSpecified.All);
    }

    /// <summary>
    ///  This method initializes the scaling bits for this control based on the bounds.
    /// </summary>
    private void InitScaling(BoundsSpecified specified)
    {
        _requiredScaling |= (byte)((int)specified & RequiredScalingMask);
    }

    /// <summary>
    ///  Sets the text and background colors of the DC, and returns the background HBRUSH.
    /// </summary>
    internal virtual HBRUSH InitializeDCForWmCtlColor(HDC dc, MessageId msg)
    {
        // NOTE: this message may not have originally been sent to this HWND.
        if (!GetStyle(ControlStyles.UserPaint))
        {
            PInvokeCore.SetTextColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(ForeColor));
            PInvokeCore.SetBkColor(dc, (COLORREF)(uint)ColorTranslator.ToWin32(BackColor));
            return BackColorBrush;
        }

        return (HBRUSH)PInvokeCore.GetStockObject(GET_STOCK_OBJECT_FLAGS.NULL_BRUSH);
    }

    /// <summary>
    ///  Invalidates a region of the control and causes a paint message
    ///  to be sent to the control. This will not force a synchronous paint to
    ///  occur, calling update after invalidate will force a
    ///  synchronous paint.
    /// </summary>
    public void Invalidate(Region? region)
    {
        Invalidate(region, false);
    }

    /// <summary>
    ///  Invalidates a region of the control and causes a paint message
    ///  to be sent to the control. This will not force a synchronous paint to
    ///  occur, calling update after invalidate will force a
    ///  synchronous paint.
    /// </summary>
    public unsafe void Invalidate(Region? region, bool invalidateChildren)
    {
        if (region is null)
        {
            Invalidate(invalidateChildren);
        }
        else if (IsHandleCreated)
        {
            using Graphics graphics = CreateGraphicsInternal();
            using RegionScope regionHandle = region.GetRegionScope(graphics);

            if (invalidateChildren)
            {
                PInvoke.RedrawWindow(
                    this,
                    lprcUpdate: null,
                    regionHandle,
                    REDRAW_WINDOW_FLAGS.RDW_INVALIDATE | REDRAW_WINDOW_FLAGS.RDW_ERASE | REDRAW_WINDOW_FLAGS.RDW_ALLCHILDREN);
            }
            else
            {
                // It's safe to invoke InvalidateRgn from a separate thread.
                using var scope = MultithreadSafeCallScope.Create();
                PInvoke.InvalidateRgn(
                    this,
                    regionHandle,
                    !GetStyle(ControlStyles.Opaque));
            }

            OnInvalidated(new InvalidateEventArgs(Rectangle.Ceiling(region.GetBounds(graphics))));
        }
    }

    /// <summary>
    ///  Invalidates the control and causes a paint message to be sent to the control.
    ///  This will not force a synchronous paint to occur, calling update after
    ///  invalidate will force a synchronous paint.
    /// </summary>
    public void Invalidate()
    {
        Invalidate(false);
    }

    /// <summary>
    ///  Invalidates the control and causes a paint message to be sent to the control.
    ///  This will not force a synchronous paint to occur, calling update after
    ///  invalidate will force a synchronous paint.
    /// </summary>
    public unsafe void Invalidate(bool invalidateChildren)
    {
        if (IsHandleCreated)
        {
            if (invalidateChildren)
            {
                PInvoke.RedrawWindow(
                    _window,
                    lprcUpdate: null,
                    HRGN.Null,
                    REDRAW_WINDOW_FLAGS.RDW_INVALIDATE | REDRAW_WINDOW_FLAGS.RDW_ERASE | REDRAW_WINDOW_FLAGS.RDW_ALLCHILDREN);
            }
            else
            {
                // It's safe to invoke InvalidateRect from a separate thread.
                using var scope = MultithreadSafeCallScope.Create();
                PInvoke.InvalidateRect(
                    this,
                    lpRect: null,
                    bErase: !_controlStyle.HasFlag(ControlStyles.Opaque));
            }

            NotifyInvalidate(ClientRectangle);
        }
    }

    /// <summary>
    ///  Invalidates a rectangular region of the control and causes a paint message
    ///  to be sent to the control. This will not force a synchronous paint to
    ///  occur, calling update after invalidate will force a
    ///  synchronous paint.
    /// </summary>
    public void Invalidate(Rectangle rc)
    {
        Invalidate(rc, false);
    }

    /// <summary>
    ///  Invalidates a rectangular region of the control and causes a paint message
    ///  to be sent to the control. This will not force a synchronous paint to
    ///  occur, calling update after invalidate will force a
    ///  synchronous paint.
    /// </summary>
    public unsafe void Invalidate(Rectangle rc, bool invalidateChildren)
    {
        if (rc.IsEmpty)
        {
            Invalidate(invalidateChildren);
        }
        else if (IsHandleCreated)
        {
            RECT rcArea = rc;
            if (invalidateChildren)
            {
                PInvoke.RedrawWindow(
                    _window,
                    &rcArea,
                    HRGN.Null,
                    REDRAW_WINDOW_FLAGS.RDW_INVALIDATE | REDRAW_WINDOW_FLAGS.RDW_ERASE | REDRAW_WINDOW_FLAGS.RDW_ALLCHILDREN);
            }
            else
            {
                // It's safe to invoke InvalidateRect from a separate thread.
                using var scope = MultithreadSafeCallScope.Create();
                PInvoke.InvalidateRect(
                    this,
                    &rcArea,
                    bErase: !_controlStyle.HasFlag(ControlStyles.Opaque));
            }

            NotifyInvalidate(rc);
        }
    }

    /// <summary>
    ///  Executes the specified delegate on the thread that owns the control's underlying window handle.
    /// </summary>
    /// <param name="method">A delegate that contains a method to be called in the control's thread context.</param>
    public void Invoke(Action method)
    {
        _ = Invoke(method, null);
    }

    /// <summary>
    ///  Executes the given delegate on the thread that owns this Control's
    ///  underlying window handle. It is an error to call this on the same thread that
    ///  the control belongs to. If the control's handle doesn't exist yet, this will
    ///  follow up the control's parent chain until it finds a control or form that does
    ///  have a window handle. If no appropriate handle can be found, invoke will throw
    ///  an exception. Exceptions that are raised during the call will be
    ///  propagated back to the caller.
    ///
    ///  There are five functions on a control that are safe to call from any
    ///  thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
    ///  For all other method calls, you should use one of the invoke methods to marshal
    ///  the call to the control's thread.
    /// </summary>
    public object Invoke(Delegate method) => Invoke(method, null);

    /// <summary>
    ///  Executes the given delegate on the thread that owns this Control's
    ///  underlying window handle. It is an error to call this on the same thread that
    ///  the control belongs to. If the control's handle doesn't exist yet, this will
    ///  follow up the control's parent chain until it finds a control or form that does
    ///  have a window handle. If no appropriate handle can be found, invoke will throw
    ///  an exception. Exceptions that are raised during the call will be
    ///  propagated back to the caller.
    ///
    ///  There are five functions on a control that are safe to call from any
    ///  thread:  GetInvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics.
    ///  For all other method calls, you should use one of the invoke methods to marshal
    ///  the call to the control's thread.
    /// </summary>
    public object Invoke(Delegate method, params object?[]? args)
    {
        using var scope = MultithreadSafeCallScope.Create();
        Control marshaler = FindMarshalingControl();
        return marshaler.MarshaledInvoke(this, method, args, synchronous: true);
    }

    /// <summary>
    ///  Executes the specified delegate on the thread that owns the control's underlying window handle.
    /// </summary>
    /// <typeparam name="T">The return type of the <paramref name="method"/>.</typeparam>
    /// <param name="method">A function to be called in the control's thread context.</param>
    /// <returns>The return value from the function being invoked.</returns>
    public T Invoke<T>(Func<T> method) => (T)Invoke(method, null);

    /// <summary>
    ///  Perform the callback of a particular ThreadMethodEntry - called by InvokeMarshaledCallbacks below.
    ///
    ///  If the invoke request originated from another thread, we should have already captured the ExecutionContext
    ///  of that thread. The callback is then invoked using that ExecutionContext (which includes info like the
    ///  compressed security stack).
    ///
    ///  NOTE: The one part of the ExecutionContext that we DON'T want applied to the callback is its SyncContext,
    ///  since this is the SyncContext of the other thread. So we grab the SyncContext of OUR thread, and pass
    ///  this through to the callback to use instead.
    ///
    ///  When the invoke request comes from this thread, there won't be an ExecutionContext so we just invoke
    ///  the callback as is.
    /// </summary>
    private static void InvokeMarshaledCallback(ThreadMethodEntry tme)
    {
        if (tme._executionContext is not null)
        {
            s_invokeMarshaledCallbackHelperDelegate ??= new ContextCallback(InvokeMarshaledCallbackHelper);

            // If there's no ExecutionContext, make sure we have a SynchronizationContext. There's no
            // direct check for ExecutionContext: this is as close as we can get.
            if (SynchronizationContext.Current is null)
            {
                WindowsFormsSynchronizationContext.InstallIfNeeded();
            }

            tme._syncContext = SynchronizationContext.Current;
            ExecutionContext.Run(tme._executionContext, s_invokeMarshaledCallbackHelperDelegate, tme);
        }
        else
        {
            InvokeMarshaledCallbackHelper(tme);
        }
    }

    /// <summary>
    ///  Worker for invoking marshaled callbacks.
    /// </summary>
    private static void InvokeMarshaledCallbackHelper(object? obj)
    {
        ThreadMethodEntry? tme = (ThreadMethodEntry?)obj;

        if (tme?._syncContext is not null)
        {
            SynchronizationContext? oldContext = SynchronizationContext.Current;

            try
            {
                SynchronizationContext.SetSynchronizationContext(tme._syncContext);

                InvokeMarshaledCallbackDo(tme);
            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(oldContext);
            }
        }
        else
        {
            InvokeMarshaledCallbackDo(tme);
        }
    }

    private static void InvokeMarshaledCallbackDo(ThreadMethodEntry? tme)
    {
        if (tme is null)
        {
            return;
        }

        // We short-circuit a couple of common cases for speed.
        if (tme._method is EventHandler handler)
        {
            if (tme._args is null || tme._args.Length < 1)
            {
                handler(tme._caller, EventArgs.Empty);
            }
            else if (tme._args.Length < 2)
            {
                handler(tme._args[0], EventArgs.Empty);
            }
            else
            {
                handler(tme._args[0], (EventArgs)tme._args[1]!);
            }
        }
        else if (tme._method is MethodInvoker invoker)
        {
            invoker();
        }
        else if (tme._method is Action action)
        {
            action();
        }
        else if (tme._method is WaitCallback waitCallback)
        {
            Debug.Assert(tme._args!.Length == 1, "Arguments are wrong for WaitCallback");
            waitCallback(tme._args[0]);
        }
        else if (tme._method is SendOrPostCallback sendOrPostCallback)
        {
            Debug.Assert(tme._args!.Length == 1, "Arguments are wrong for SendOrPostCallback");
            sendOrPostCallback(tme._args[0]);
        }
        else
        {
            tme._retVal = tme._method!.DynamicInvoke(tme._args);
        }
    }

    /// <summary>
    ///  Called on the control's owning thread to perform the actual callback.
    ///  This empties this control's callback queue, propagating any exceptions
    ///  back as needed.
    /// </summary>
    private void InvokeMarshaledCallbacks()
    {
        ThreadMethodEntry? current = null;
        if (_threadCallbackList is not null)
        {
            lock (_threadCallbackList)
            {
                if (_threadCallbackList.Count > 0)
                {
                    current = _threadCallbackList.Dequeue();
                }
            }
        }

        // Now invoke on all the queued items.
        while (current is not null)
        {
            if (current._method is not null)
            {
                try
                {
                    // If we are running under the debugger, don't wrap asynchronous
                    // calls in a try catch. It is much better to throw here than pop up
                    // a thread exception dialog below.
                    if (NativeWindow.WndProcShouldBeDebuggable && !current._synchronous)
                    {
                        InvokeMarshaledCallback(current);
                    }
                    else
                    {
                        try
                        {
                            InvokeMarshaledCallback(current);
                        }
                        catch (Exception t)
                        {
                            current._exception = t.GetBaseException();
                        }
                    }
                }
                finally
                {
                    current.Complete();

                    // This code matches the behavior above. Basically, if we're debugging, don't
                    // do this because the exception would have been handled above. If we're
                    // not debugging, raise the exception here.
                    if (!NativeWindow.WndProcShouldBeDebuggable
                        && current._exception is not null
                        && !current._synchronous)
                    {
                        Application.OnThreadException(current._exception);
                    }
                }
            }

            if (_threadCallbackList is not null)
            {
                lock (_threadCallbackList)
                {
                    current = _threadCallbackList.Count > 0 ? _threadCallbackList.Dequeue() : null;
                }
            }
        }
    }

    protected void InvokePaint(Control c, PaintEventArgs e)
    {
        c.OnPaint(e);
    }

    protected void InvokePaintBackground(Control c, PaintEventArgs e)
    {
        c.OnPaintBackground(e);
    }

    /// <summary>
    ///  Determines whether the font is explicitly set.
    /// </summary>
    internal bool IsFontSet() => Properties.ContainsKey(s_fontProperty);

    /// <summary>
    ///  Returns <see langword="true"/> if <paramref name="descendant"/> is a descendant of this <see cref="Control"/>.
    /// </summary>
    internal bool IsDescendant(Control? descendant)
    {
        Control? control = descendant;
        while (control is not null)
        {
            if (control == this)
            {
                return true;
            }

            control = control.ParentInternal;
        }

        return false;
    }

    /// <summary>
    ///  Determines whether the CAPS LOCK, NUM LOCK, or SCROLL LOCK key is in effect.
    /// </summary>
    public static bool IsKeyLocked(Keys keyVal)
    {
        if (keyVal is Keys.Insert or Keys.NumLock or Keys.CapsLock or Keys.Scroll)
        {
            int result = PInvoke.GetKeyState((int)keyVal);

            // If the high-order bit is 1, the key is down; otherwise, it is up.
            // If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key,
            // is toggled if it is turned on. The key is off and untoggled if the low-order bit is 0.
            // A toggle key's indicator light (if any) on the keyboard will be on when the key is toggled,
            // and off when the key is untoggled.

            // Toggle keys (only low bit is of interest).
            return keyVal is Keys.Insert or Keys.CapsLock ? (result & 0x1) != 0x0 : (result & 0x8001) != 0x0;
        }

        // else - it's an un-lockable key.
        // Actually get the exception string from the system resource.
        throw new NotSupportedException(SR.ControlIsKeyLockedNumCapsScrollLockKeysSupportedOnly);
    }

    /// <summary>
    ///  Determines if <paramref name="charCode"/> is an input character that the control wants.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called during window message pre-processing to determine whether the given input
    ///   character should be pre-processed or sent directly to the control. The pre-processing of a character
    ///   includes checking whether the character is a mnemonic of another control.
    ///   (<see cref="PreProcessControlMessage(ref Message)"/>)
    ///  </para>
    /// </remarks>
    /// <returns>
    ///  'true' if the <paramref name="charCode"/> should be sent directly to the control.
    /// </returns>
    protected virtual bool IsInputChar(char charCode)
    {
        int mask = charCode == (char)(int)Keys.Tab
            ? (int)(PInvoke.DLGC_WANTCHARS | PInvoke.DLGC_WANTALLKEYS | PInvoke.DLGC_WANTTAB)
            : (int)(PInvoke.DLGC_WANTCHARS | PInvoke.DLGC_WANTALLKEYS);

        return ((int)PInvokeCore.SendMessage(this, PInvokeCore.WM_GETDLGCODE) & mask) != 0;
    }

    /// <summary>
    ///  Determines if <paramref name="keyData"/> is an input key that the control wants.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called during window message pre-processing to determine whether the given input key
    ///   should be pre-processed or sent directly to the control.Keys that are pre-processed include TAB, RETURN,
    ///   ESCAPE, and arrow keys. (<see cref="PreProcessControlMessage(ref Message)"/>)
    ///  </para>
    /// </remarks>
    /// <returns>
    ///  'true' if the <paramref name="keyData"/> should be sent directly to the control.
    /// </returns>
    protected virtual bool IsInputKey(Keys keyData)
    {
        if ((keyData & Keys.Alt) == Keys.Alt)
        {
            return false;
        }

        uint mask = PInvoke.DLGC_WANTALLKEYS;
        switch (keyData & Keys.KeyCode)
        {
            case Keys.Tab:
                mask = PInvoke.DLGC_WANTALLKEYS | PInvoke.DLGC_WANTTAB;
                break;
            case Keys.Left:
            case Keys.Right:
            case Keys.Up:
            case Keys.Down:
                mask = PInvoke.DLGC_WANTALLKEYS | PInvoke.DLGC_WANTARROWS;
                break;
        }

        return IsHandleCreated
            && ((uint)PInvokeCore.SendMessage(this, PInvokeCore.WM_GETDLGCODE) & mask) != 0;
    }

    /// <summary>
    ///  Determines if <paramref name="charCode"/> is the mnemonic character in <paramref name="text"/>.
    ///  The mnemonic character is the character immediately following the first
    ///  instance of "&amp;" in text
    /// </summary>
    public static bool IsMnemonic(char charCode, string? text)
    {
        // Special case handling:
        if (charCode == '&')
        {
            return false;
        }

        if (text is not null)
        {
            int pos = -1; // start with -1 to handle double &'s
            char c2 = char.ToUpper(charCode, CultureInfo.CurrentCulture);
            for (; ; )
            {
                if (pos + 1 >= text.Length)
                {
                    break;
                }

                pos = text.IndexOf('&', pos + 1) + 1;
                if (pos <= 0 || pos >= text.Length)
                {
                    break;
                }

                char c1 = char.ToUpper(text[pos], CultureInfo.CurrentCulture);

                if (c1 == c2 || char.ToLower(c1, CultureInfo.CurrentCulture) == char.ToLower(c2, CultureInfo.CurrentCulture))
                {
                    return true;
                }
            }
        }

        return false;
    }

    // Checks if this is a container control and will be scaled by parent.
    private static bool IsScaledByParent(Control control)
    {
        Control? parentControl = control.Parent;
        while (parentControl is not null and not ContainerControl)
        {
            parentControl = parentControl.Parent;
        }

        return parentControl is ContainerControl;
    }

    /// <summary>
    ///  Transforms an integer coordinate from logical to device units by scaling it for the current DPI
    ///  and rounding down to the nearest integer value.
    /// </summary>
    public int LogicalToDeviceUnits(int value) => ScaleHelper.ScaleToDpi(value, DeviceDpi);

    /// <summary>
    ///  Transforms size from logical to device units by scaling it for the current
    ///  Dpi and rounding down to the nearest integer value for width and height.
    /// </summary>
    /// <param name="value"> size to be scaled</param>
    /// <returns> scaled size</returns>
    public Size LogicalToDeviceUnits(Size value) => ScaleHelper.ScaleToDpi(value, DeviceDpi);

    /// <summary>
    ///  Create a new bitmap scaled for the device units. When displayed on the device,
    ///  the scaled image will have same size as the original image would have when
    ///  displayed at 96dpi.
    /// </summary>
    /// <param name="logicalBitmap">The image to scale from logical units to device units</param>
    public void ScaleBitmapLogicalToDevice(ref Bitmap logicalBitmap)
    {
        if (logicalBitmap is null)
        {
            return;
        }

        logicalBitmap = ScaleHelper.ScaleToDpi(logicalBitmap, DeviceDpi, disposeBitmap: true);
    }

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

    private static void AdjustWindowRectExForDpi(ref RECT rect, WINDOW_STYLE style, bool bMenu, WINDOW_EX_STYLE exStyle, int dpi)
    {
        if ((ScaleHelper.IsThreadPerMonitorV2Aware || ScaleHelper.IsScalingRequired) && OsVersion.IsWindows10_1703OrGreater())
        {
            PInvoke.AdjustWindowRectExForDpi(ref rect, style, bMenu, exStyle, (uint)dpi);
        }
        else
        {
            PInvoke.AdjustWindowRectEx(ref rect, style, bMenu, exStyle);
        }
    }

    private object MarshaledInvoke(Control caller, Delegate method, object?[]? args, bool synchronous)
    {
        // Marshaling an invoke occurs in three steps:
        //
        // 1. Create a ThreadMethodEntry that contains the packet of information
        //     about this invoke. This TME is placed on a linked list of entries because
        //     we have a gap between the time we PostMessage and the time it actually
        //     gets processed, and this gap may allow other invokes to come in. Access
        //     to this linked list is always synchronized.
        //
        // 2. Post ourselves a message. Our caller has already determined the
        //     best control to call us on, and we should almost always have a handle.
        //
        // 3. If we're synchronous, wait for the message to get processed. We don't do
        //     a SendMessage here so we're compatible with OLE, which will abort many
        //     types of calls if we're within a SendMessage.

        if (!IsHandleCreated)
        {
            throw new InvalidOperationException(SR.ErrorNoMarshalingThread);
        }

        // We don't want to wait if we're on the same thread, or else we'll deadlock.
        // It is important that syncSameThread always be false for asynchronous calls.
        bool syncSameThread = synchronous && PInvoke.GetWindowThreadProcessId(this, out _) == PInvokeCore.GetCurrentThreadId();

        // Store the compressed stack information from the thread that is calling the Invoke()
        // so we can assign the same security context to the thread that will actually execute
        // the delegate being passed.
        ExecutionContext? executionContext = null;
        if (!syncSameThread)
        {
            executionContext = ExecutionContext.Capture();
        }

        ThreadMethodEntry tme = new(
            caller,
            this,
            method,
            args,
            synchronous,
            executionContext);

        lock (this)
        {
            _threadCallbackList ??= new Queue<ThreadMethodEntry>();
        }

        lock (_threadCallbackList)
        {
            if (s_threadCallbackMessage == PInvokeCore.WM_NULL)
            {
                s_threadCallbackMessage = PInvoke.RegisterWindowMessage($"{Application.WindowMessagesVersion}_ThreadCallbackMessage");
            }

            _threadCallbackList.Enqueue(tme);
        }

        if (syncSameThread)
        {
            InvokeMarshaledCallbacks();
        }
        else
        {
            PInvokeCore.PostMessage(this, s_threadCallbackMessage);
        }

        if (synchronous)
        {
            if (!tme.IsCompleted)
            {
                // In synchronous call we not need waitHandle after wait.
                using WaitHandle waitHandle = tme.AsyncWaitHandle;
                WaitForWaitHandle(waitHandle);
            }

            if (tme._exception is not null)
            {
                ExceptionDispatchInfo.Throw(tme._exception);
            }

            return tme._retVal!;
        }
        else
        {
            return tme;
        }
    }

    /// <summary>
    ///  This method is used by WM_GETCONTROLNAME and WM_GETCONTROLTYPE
    ///  to marshal a string to a message structure. It handles
    ///  two cases:  if no buffer was passed it returns the size of
    ///  buffer needed. If a buffer was passed, it fills the buffer.
    ///  If the passed buffer is not long enough it will return -1.
    /// </summary>
    private static void MarshalStringToMessage(string value, ref Message m)
    {
        if (m.LParamInternal == 0)
        {
            m.ResultInternal = (LRESULT)((value.Length + 1) * sizeof(char));
            return;
        }

        if ((int)m.WParamInternal < value.Length + 1)
        {
            m.ResultInternal = (LRESULT)(-1);
            return;
        }

        // Copy the name into the given IntPtr
        char[] nullChar = [(char)0];
        byte[] nullBytes;
        byte[] bytes;

        bytes = Encoding.Unicode.GetBytes(value);
        nullBytes = Encoding.Unicode.GetBytes(nullChar);

        Marshal.Copy(bytes, 0, m.LParamInternal, bytes.Length);
        Marshal.Copy(nullBytes, 0, m.LParamInternal + (nint)bytes.Length, nullBytes.Length);

        m.ResultInternal = (LRESULT)((bytes.Length + nullBytes.Length) / sizeof(char));
    }

    /// <summary>
    ///  Propagates the invalidation event, notifying the control that
    ///  some part of it is being invalidated and will subsequently need
    ///  to repaint.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void NotifyInvalidate(Rectangle invalidatedArea)
    {
        OnInvalidated(new InvalidateEventArgs(invalidatedArea));
    }

    /// <summary>
    ///  Called by the NativeWindow callback when an exception is caught.
    /// </summary>
    /// <returns>true, when the exception has been handled; otherwise, false.</returns>
    /// <remarks>
    ///  <para>
    ///   This will usually be rerouted to the <see cref="Application.ThreadException"/>, but in certain
    ///   (async) scenarios, it makes more sense to catch the exception on the control or form that caused it.
    ///  </para>
    ///  <para>
    ///   Example: When a form throws an exception inside of Form.OnLoad, and the scenario needs to be gracefully handled
    ///   when another form shows the erroneous form. In that case, you cannot directly catch the exception.
    ///  </para>
    ///  <para>
    ///   It gets marshalled down to <see cref="Application.ThreadException"/>, which is an extreme
    ///   discoverability issue and the root cause for many WinForms apps crashing unexpectedly.
    ///  </para>
    /// </remarks>
    private protected virtual bool SuppressApplicationOnThreadException(Exception ex) => false;

    // Used by form to notify the control that it is validating.
    private bool NotifyValidating()
    {
        CancelEventArgs ev = new();
        OnValidating(ev);
        return ev.Cancel;
    }

    // Used by form to notify the control that it has been validated.
    private void NotifyValidated()
    {
        OnValidated(EventArgs.Empty);
    }

    /// <summary>
    ///  Raises the <see cref="Click"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void InvokeOnClick(Control? toInvoke, EventArgs e)
    {
        toInvoke?.OnClick(e);
    }

    protected virtual void OnAutoSizeChanged(EventArgs e)
    {
        if (Events[s_autoSizeChangedEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnBackColorChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        if (Properties.TryGetValue(s_backBrushProperty, out HBRUSH backBrush))
        {
            if (GetState(States.OwnCtlBrush))
            {
                if (!backBrush.IsNull)
                {
                    PInvokeCore.DeleteObject(backBrush);
                }
            }

            Properties.RemoveValue(s_backBrushProperty);
        }

        Invalidate();

        if (Events[s_backColorEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentBackColorChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnBackgroundImageChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        Invalidate();

        if (Events[s_backgroundImageEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentBackgroundImageChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnBackgroundImageLayoutChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        Invalidate();

        if (Events[s_backgroundImageLayoutEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnBindingContextChanged(EventArgs e)
    {
        if (Properties.ContainsKey(s_bindingsProperty))
        {
            if (!Binding.IsSupported)
            {
                throw new NotSupportedException(SR.BindingNotSupported);
            }

            UpdateBindings();
        }

        if (Events[s_bindingContextEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentBindingContextChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnCausesValidationChanged(EventArgs e)
    {
        if (Events[s_causesValidationEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Called when a child is about to resume its layout. The default implementation
    ///  calls OnChildLayoutResuming on the parent.
    /// </summary>
    internal virtual void OnChildLayoutResuming(Control child, bool performLayout)
    {
        ParentInternal?.OnChildLayoutResuming(child, performLayout);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnContextMenuStripChanged(EventArgs e)
    {
        if (Events[s_contextMenuStripEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnCursorChanged(EventArgs e)
    {
        if (Events[s_cursorEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentCursorChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDataContextChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        if (Events[s_dataContextEvent] is EventHandler eventHandler)
        {
            eventHandler(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentDataContextChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDockChanged(EventArgs e)
    {
        if (Events[s_dockEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Raises the <see cref="Enabled"/> event.
    ///
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.OnEnabled to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnEnabledChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        if (IsHandleCreated)
        {
            PInvoke.EnableWindow(this, Enabled);

            // User-paint controls should repaint when their enabled state changes
            if (GetStyle(ControlStyles.UserPaint))
            {
                Invalidate();
                Update();
            }
        }

        if (Events[s_enabledEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentEnabledChanged(e);
            }
        }
    }

    private protected virtual void OnFrameWindowActivate(bool fActivate)
    {
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnFontChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        Invalidate();

        if (Properties.ContainsKey(s_fontHeightProperty))
        {
            Properties.AddValue(s_fontHeightProperty, -1);
        }

        // Cleanup any font handle wrapper.
        DisposeFontHandle();

        if (IsHandleCreated)
        {
            SetWindowFont();
        }

        if (Events[s_fontEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            using (new LayoutTransaction(this, this, PropertyNames.Font, resumeLayout: false))
            {
                // This may have changed the sizes of our children.
                for (int i = 0; i < children.Count; i++)
                {
                    children[i].OnParentFontChanged(e);
                }
            }
        }

        LayoutTransaction.DoLayout(this, this, PropertyNames.Font);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnForeColorChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        Invalidate();

        if (Events[s_foreColorEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentForeColorChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnRightToLeftChanged(EventArgs e)
    {
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        // update the scroll position when the handle has been created
        // MUST SET THIS BEFORE CALLING RecreateHandle!!!
        SetExtendedState(ExtendedStates.SetScrollPosition, true);

        RecreateHandle();

        if (Events[s_rightToLeftEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentRightToLeftChanged(e);
            }
        }
    }

    /// <summary>
    ///  OnNotifyMessage is called if the ControlStyles.EnableNotifyMessage bit is set.
    ///  This allows for controls to listen to window messages, without allowing them to
    ///  actually modify the message.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnNotifyMessage(Message m)
    {
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentBackColorChanged(EventArgs e)
    {
        Color backColor = Properties.GetValueOrDefault<Color>(s_backColorProperty);
        if (backColor.IsEmpty)
        {
            OnBackColorChanged(e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentBackgroundImageChanged(EventArgs e)
    {
        OnBackgroundImageChanged(e);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentBindingContextChanged(EventArgs e)
    {
        if (!Properties.ContainsKey(s_bindingManagerProperty))
        {
            OnBindingContextChanged(e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentCursorChanged(EventArgs e)
    {
        if (!Properties.ContainsKey(s_cursorProperty))
        {
            OnCursorChanged(e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentDataContextChanged(EventArgs e)
    {
        if (Properties.ContainsKey(s_dataContextProperty))
        {
            if (Equals(Properties.GetValueOrDefault<object>(s_dataContextProperty), Parent?.DataContext))
            {
                // Same as the parent context, make it ambient by removing it.
                Properties.RemoveValue(s_dataContextProperty);

                // Even though internally we don't store it any longer, and the
                // value we had stored therefore changed, technically the value
                // remains the same, so we don't raise the DataContextChanged event.
                return;
            }
        }

        // In every other case we're going to raise the event.
        OnDataContextChanged(e);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentEnabledChanged(EventArgs e)
    {
        if (GetState(States.Enabled))
        {
            OnEnabledChanged(e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentFontChanged(EventArgs e)
    {
        // Container controls that were marked IsDpiChangeScalingRequired had to go through OnFontChanged event
        // Irrespective of the Font status (explicit set or inherit Font). See "WmDpiChangedBeforeParent" for more info.
        var container = this as ContainerControl;

        try
        {
            if (!IsFontSet() || (container is not null && container.IsDpiChangeScalingRequired))
            {
                OnFontChanged(e);
            }
        }
        finally
        {
            if (container is not null)
            {
                container.IsDpiChangeScalingRequired = false;
            }
        }
    }

    /// <summary>
    ///  Occurs when the parent of this control has recreated its handle.
    /// </summary>
    internal virtual void OnParentHandleRecreated()
    {
        // Restore ourselves over to the original control.
        // Use SetParent directly so as to not raise ParentChanged events.
        Control? parent = ParentInternal;
        if (parent is not null && IsHandleCreated)
        {
            if (PInvoke.SetParent(this, parent).IsNull)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed);
            }

            UpdateZOrder();
        }

        SetState(States.ParentRecreating, false);

        // If our parent was initially the parent who's handle just got recreated, we need
        // to recreate ourselves so that we get notification. See UpdateReflectParent for more details.
        if (ReflectParent == ParentInternal)
        {
            RecreateHandle();
        }
    }

    /// <summary>
    ///  Occurs when the parent of this control is recreating its handle.
    /// </summary>
    internal virtual void OnParentHandleRecreating()
    {
        SetState(States.ParentRecreating, true);

        // Move this control over to the parking window.

        // If we left it parented to the parent control, DestroyWindow would force us to destroy our handle as well.
        // Temporarily parenting to the parking window avoids having to recreate our handle from scratch.

        if (IsHandleCreated)
        {
            Application.ParkHandle(handle: new(this), DpiAwarenessContext);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentForeColorChanged(EventArgs e)
    {
        Color foreColor = Properties.GetValueOrDefault<Color>(s_foreColorProperty);
        if (foreColor.IsEmpty)
        {
            OnForeColorChanged(e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentRightToLeftChanged(EventArgs e)
    {
        if (!Properties.TryGetValue(s_rightToLeftProperty, out RightToLeft rightToLeft) || rightToLeft == RightToLeft.Inherit)
        {
            OnRightToLeftChanged(e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentVisibleChanged(EventArgs e)
    {
        if (DesiredVisibility)
        {
            OnVisibleChanged(e);
        }
    }

    // OnVisibleChanged/OnParentVisibleChanged is not called when a parent becomes invisible
    internal virtual void OnParentBecameInvisible()
    {
        if (DesiredVisibility && ChildControls is { } children)
        {
            // This control became invisible too - notify its children

            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentBecameInvisible();
            }
        }
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnPrint(PaintEventArgs e)
    {
        ArgumentNullException.ThrowIfNull(e);

        if (GetStyle(ControlStyles.UserPaint))
        {
            // Theme support requires that we paint the background and foreground to support semi-transparent children
            PaintWithErrorHandling(e, PaintLayerBackground);
            e.ResetGraphics();
            PaintWithErrorHandling(e, PaintLayerForeground);
        }
        else
        {
            if (e is not PrintPaintEventArgs ppev)
            {
                uint flags = PInvoke.PRF_CHILDREN | PInvoke.PRF_CLIENT | PInvoke.PRF_ERASEBKGND | PInvoke.PRF_NONCLIENT;

                using DeviceContextHdcScope hdc = new(e);
                Message m = Message.Create(HWND, PInvokeCore.WM_PRINTCLIENT, (WPARAM)hdc, (LPARAM)flags);
                DefWndProc(ref m);
            }
            else
            {
                Message m = ppev.Message;
                DefWndProc(ref m);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnTabIndexChanged(EventArgs e)
    {
        if (Events[s_tabIndexEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnTabStopChanged(EventArgs e)
    {
        if (Events[s_tabStopEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnTextChanged(EventArgs e)
    {
        if (Events[s_textEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Raises the <see cref="Visible"/> event.
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.OnVisible to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnVisibleChanged(EventArgs e)
    {
        bool visible = Visible;
        if (visible)
        {
            UnhookMouseEvent();
            _trackMouseEvent = default;
        }

        if (_parent is not null && visible && !Created)
        {
            bool isDisposing = GetAnyDisposingInHierarchy();
            if (!isDisposing)
            {
                // Usually the control is created by now, but in a few corner cases
                // exercised by the PropertyGrid dropdowns, it isn't
                CreateControl();
            }
        }

        if (Events[s_visibleEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                Control child = children[i];
                if (child.Visible)
                {
                    child.OnParentVisibleChanged(e);
                }

                if (!visible)
                {
                    child.OnParentBecameInvisible();
                }
            }
        }
    }

    internal virtual void OnTopMostActiveXParentChanged(EventArgs e)
    {
        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnTopMostActiveXParentChanged(e);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnParentChanged(EventArgs e)
    {
        if (Events[s_parentEvent] is EventHandler eh)
        {
            eh(this, e);
        }

        // Inform the control that the topmost control is now an ActiveX control
        if (TopMostParent.IsActiveX)
        {
            OnTopMostActiveXParentChanged(EventArgs.Empty);
        }
    }

    /// <summary>
    ///  Raises the <see cref="Click"/>
    ///  event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnClick(EventArgs e)
    {
        ((EventHandler?)Events[s_clickEvent])?.Invoke(this, e);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnClientSizeChanged(EventArgs e)
    {
        if (Events[s_clientSizeEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Raises the <see cref="ControlAdded"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnControlAdded(ControlEventArgs e)
    {
        ((ControlEventHandler?)Events[s_controlAddedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="ControlRemoved"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnControlRemoved(ControlEventArgs e)
    {
        ((ControlEventHandler?)Events[s_controlRemovedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Called when the control is first created.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnCreateControl()
    {
    }

    /// <summary>
    ///  Inheriting classes should override this method to find out when the handle has been created.
    ///  Call base.OnHandleCreated first.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnHandleCreated(EventArgs e)
    {
        if (IsHandleCreated)
        {
            // Setting fonts is for some reason incredibly expensive.
            // (Even if you exclude font handle creation)
            if (!GetStyle(ControlStyles.UserPaint))
            {
                SetWindowFont();
            }

            HandleHighDpi();

            // Restore drag drop status. Ole Initialize happens when the ThreadContext in Application is created.
            SetAcceptDrops(AllowDrop);

            Region? region = Region;
            if (region is not null)
            {
                SetRegionInternal(region);
            }

            // Cache Handle in a local before asserting so we minimize code running under the Assert.
            IntPtr handle = Handle;

            // The Accessibility Object for this Control
            if (Properties.TryGetValue(s_accessibilityProperty, out ControlAccessibleObject? clientAccessibleObject))
            {
                clientAccessibleObject.Handle = handle;
            }

            // Private accessibility object for control, used to wrap the object that
            // OLEACC.DLL creates to represent the control's non-client (NC) region.
            if (Properties.TryGetValue(s_ncAccessibilityProperty, out ControlAccessibleObject? nonClientAccessibleObject))
            {
                nonClientAccessibleObject.Handle = handle;
            }

            // Set the window text from the Text property.
            if (_text is not null && _text.Length != 0)
            {
                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)
                && !GetExtendedState(ExtendedStates.HaveInvoked))
            {
                BeginInvoke(new EventHandler(OnSetScrollPosition));
                SetExtendedState(ExtendedStates.HaveInvoked, true);
                SetExtendedState(ExtendedStates.SetScrollPosition, false);
            }
        }

        ((EventHandler?)Events[s_handleCreatedEvent])?.Invoke(this, e);

        if (IsHandleCreated)
        {
            // Now, repost the thread callback message if we found it. We should do this last, so we're as close
            // to the same state as when the message was placed.
            if (GetState(States.ThreadMarshalPending))
            {
                PInvokeCore.PostMessage(this, s_threadCallbackMessage);
                SetState(States.ThreadMarshalPending, false);
            }
        }

        void HandleHighDpi()
        {
            if (!DpiAwarenessContext.IsEquivalent(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
            {
                return;
            }

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

            if (fontDpi != _deviceDpi)
            {
                // 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);
                ScaledControlFont = fontForDpi;

                // If it is a container control that inherit Font and is scaled by parent, we simply scale Font
                // and wait for OnFontChangedEvent caused by its parent. Otherwise, we scale Font and trigger
                // 'OnFontChanged' event explicitly. ex: Windows Forms designer natively hosted in VS.
                if (IsFontSet())
                {
                    SetScaledFont(fontForDpi);
                }
            }

            RescaleConstantsForDpi(old, _deviceDpi);

            // 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,
            // would need recalculated to place it correctly.
            if (this is Form form && form.TopLevel)
            {
                // Form gets location information from CreateParams but the values are calculated before the handle creation.
                // When launching the Form on a secondary monitor, DPI is evaluated only after handle is created. for the Form and the
                // Form resized according to the new DPI.Hence, Form location need to be recalculated with new bounds information.
                form.AdjustFormPosition();
            }
        }
    }

    private void OnSetScrollPosition(object? sender, EventArgs e)
    {
        SetExtendedState(ExtendedStates.HaveInvoked, false);
        OnInvokedSetScrollPosition(sender, e);
    }

    internal virtual unsafe void OnInvokedSetScrollPosition(object? sender, EventArgs e)
    {
        if (this is not ScrollableControl && !IsMirrored)
        {
            SCROLLINFO si = new()
            {
                cbSize = (uint)sizeof(SCROLLINFO),
                fMask = SCROLLINFO_MASK.SIF_RANGE
            };

            if (PInvoke.GetScrollInfo(this, SCROLLBAR_CONSTANTS.SB_HORZ, ref si))
            {
                si.nPos = (RightToLeft == RightToLeft.Yes) ? si.nMax : si.nMin;
                PInvokeCore.SendMessage(this, PInvokeCore.WM_HSCROLL, WPARAM.MAKEWPARAM((int)SCROLLBAR_COMMAND.SB_THUMBPOSITION, si.nPos));
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnLocationChanged(EventArgs e)
    {
        OnMove(e);
        if (Events[s_locationEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Raises the <see cref="HandleDestroyed"/> event.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   Inheriting classes should override this method to find out when the handle is about to be destroyed.
    ///   Call base.OnHandleDestroyed last.
    ///  </para>
    /// </remarks>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnHandleDestroyed(EventArgs e)
    {
        ((EventHandler?)Events[s_handleDestroyedEvent])?.Invoke(this, e);

        // The Accessibility Object for this Control
        if (Properties.TryGetValue(s_accessibilityProperty, out ControlAccessibleObject? accObj))
        {
            accObj.Handle = IntPtr.Zero;
        }

        // Private accessibility object for control, used to wrap the object that
        // OLEACC.DLL creates to represent the control's non-client (NC) region.
        if (Properties.TryGetValue(s_ncAccessibilityProperty, out ControlAccessibleObject? nonClientAccessibleObject))
        {
            nonClientAccessibleObject.Handle = IntPtr.Zero;
        }

        ReflectParent = null;

        if (!RecreatingHandle && GetState(States.OwnCtlBrush) && Properties.TryGetValue(s_backBrushProperty, out HBRUSH backBrush))
        {
            Properties.RemoveValue(s_backBrushProperty);
            if (!backBrush.IsNull)
            {
                PInvokeCore.DeleteObject(backBrush);
            }
        }

        // this code is important -- it is critical that we stash away
        // the value of the text for controls such as edit, button,
        // label, etc. Without this processing, any time you change a
        // property that forces handle recreation, you lose your text!
        // See the above code in wmCreate
        try
        {
            if (!GetAnyDisposingInHierarchy())
            {
                _text = Text;
                if (_text is not null && _text.Length == 0)
                {
                    _text = null;
                }
            }

            SetAcceptDrops(false);
        }
        catch (Exception ex) when (!ex.IsCriticalException())
        {
            // Some ActiveX controls throw exceptions when you ask for the text property after you have destroyed their
            // handle. We don't want those exceptions to bubble all the way to the top, since we leave our state in a mess.
        }
    }

    /// <summary>
    ///  Raises the <see cref="DoubleClick"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDoubleClick(EventArgs e)
    {
        ((EventHandler?)Events[s_doubleClickEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="Enter"/> event.
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onEnter to send this event to any registered event listeners.
    /// </summary>
    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onDragEnter to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDragEnter(DragEventArgs drgevent)
    {
        ((DragEventHandler?)Events[s_dragEnterEvent])?.Invoke(this, drgevent);
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onDragOver to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDragOver(DragEventArgs drgevent)
    {
        ((DragEventHandler?)Events[s_dragOverEvent])?.Invoke(this, drgevent);
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onDragLeave to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDragLeave(EventArgs e)
    {
        ((EventHandler?)Events[s_dragLeaveEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onDragDrop to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnDragDrop(DragEventArgs drgevent)
    {
        ((DragEventHandler?)Events[s_dragDropEvent])?.Invoke(this, drgevent);
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onGiveFeedback to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnGiveFeedback(GiveFeedbackEventArgs gfbevent)
    {
        ((GiveFeedbackEventHandler?)Events[s_giveFeedbackEvent])?.Invoke(this, gfbevent);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected internal virtual void OnEnter(EventArgs e)
    {
        ((EventHandler?)Events[s_enterEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="GotFocus"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void InvokeGotFocus(Control? toInvoke, EventArgs e)
    {
        if (toInvoke is not null)
        {
            toInvoke.OnGotFocus(e);
            KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(toInvoke);
        }
    }

    /// <summary>
    ///  Raises the <see cref="GotFocus"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnGotFocus(EventArgs e)
    {
        if (IsActiveX)
        {
            ActiveXOnFocus(true);
        }

        _parent?.ChildGotFocus(this);

        ((EventHandler?)Events[s_gotFocusEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onHelp to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnHelpRequested(HelpEventArgs hevent)
    {
        HelpEventHandler? handler = (HelpEventHandler?)Events[s_helpRequestedEvent];
        if (handler is not null)
        {
            handler(this, hevent);

            // Mark the event as handled so that the event isn't raised for the
            // control's parent.
            if (hevent is not null)
            {
                hevent.Handled = true;
            }
        }

        if (hevent is not null && !hevent.Handled)
        {
            ParentInternal?.OnHelpRequested(hevent);
        }
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.OnInvalidate to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnInvalidated(InvalidateEventArgs e)
    {
        // Ask the site to change the view.
        if (IsActiveX)
        {
            ActiveXViewChanged();
        }

        // Transparent control support.
        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnParentInvalidated(e);
            }
        }

        ((InvalidateEventHandler?)Events[s_invalidatedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="KeyDown"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnKeyDown(KeyEventArgs e)
    {
        ((KeyEventHandler?)Events[s_keyDownEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="KeyPress"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnKeyPress(KeyPressEventArgs e)
    {
        ((KeyPressEventHandler?)Events[s_keyPressEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="KeyUp"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnKeyUp(KeyEventArgs e)
    {
        if (OsVersion.IsWindows11_OrGreater()
            && (e.KeyCode.HasFlag(Keys.ControlKey) || e.KeyCode == Keys.Escape))
        {
            KeyboardToolTipStateMachine.HidePersistentTooltip();
        }

        ((KeyEventHandler?)Events[s_keyUpEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Core layout logic. Inheriting controls should override this function to do any custom
    ///  layout logic. It is not necessary to call base.OnLayout, however for normal docking
    ///  an functions to work, base.OnLayout must be called.
    ///  Raises the <see cref="Layout"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnLayout(LayoutEventArgs levent)
    {
        // Ask the site to change the view.
        if (IsActiveX)
        {
            ActiveXViewChanged();
        }

        ((LayoutEventHandler?)Events[s_layoutEvent])?.Invoke(this, levent);

        bool parentRequiresLayout = LayoutEngine.Layout(this, levent);
        if (parentRequiresLayout && ParentInternal is not null)
        {
            // LayoutEngine.Layout can return true to request that our parent resize us because
            // we did not have enough room for our contents. We can not just call PerformLayout
            // because this container is currently suspended. PerformLayout will check this state
            // flag and PerformLayout on our parent.
            ParentInternal.SetState(States.LayoutIsDirty, true);
        }
    }

    /// <summary>
    ///  Called when the last resume layout call is made. If performLayout is true a layout will
    ///  occur as soon as this call returns. Layout is still suspended when this call is made.
    ///  The default implementation calls OnChildLayoutResuming on the parent, if it exists.
    /// </summary>
    internal virtual void OnLayoutResuming(bool performLayout)
    {
        ParentInternal?.OnChildLayoutResuming(this, performLayout);
    }

    internal virtual void OnLayoutSuspended()
    {
    }

    /// <summary>
    ///  Raises the <see cref="Leave"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected internal virtual void OnLeave(EventArgs e)
    {
        ((EventHandler?)Events[s_leaveEvent])?.Invoke(this, e);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void InvokeLostFocus(Control? toInvoke, EventArgs e)
    {
        if (toInvoke is not null)
        {
            toInvoke.OnLostFocus(e);
            KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(toInvoke);
        }
    }

    /// <summary>
    ///  Raises the <see cref="LostFocus"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnLostFocus(EventArgs e)
    {
        if (IsActiveX)
        {
            ActiveXOnFocus(false);
        }

        ((EventHandler?)Events[s_lostFocusEvent])?.Invoke(this, e);
    }

    protected virtual void OnMarginChanged(EventArgs e)
    {
        ((EventHandler?)Events[s_marginChangedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseDoubleClick"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseDoubleClick(MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[s_mouseDoubleClickEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="OnMouseClick"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseClick(MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[s_mouseClickEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseCaptureChanged"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseCaptureChanged(EventArgs e)
    {
        ((EventHandler?)Events[s_mouseCaptureChangedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseDown"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseDown(MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[s_mouseDownEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseEnter"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseEnter(EventArgs e)
    {
        ((EventHandler?)Events[s_mouseEnterEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseLeave"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseLeave(EventArgs e)
    {
        ((EventHandler?)Events[s_mouseLeaveEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="DpiChangedBeforeParent"/> event.
    ///  Occurs when the form is moved to a monitor with a different resolution (number of dots per inch),
    ///  or when scaling level is changed in the windows setting by the user.
    ///  This message is not sent to the top level windows.
    /// </summary>
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    protected virtual void OnDpiChangedBeforeParent(EventArgs e)
    {
        ((EventHandler?)Events[s_dpiChangedBeforeParentEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="DpiChangedAfterParent"/> event.
    ///  Occurs when the form is moved to a monitor with a different resolution (number of dots per inch),
    ///  or when scaling level is changed in windows setting by the user.
    ///  This message is not sent to the top level windows.
    /// </summary>
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    protected virtual void OnDpiChangedAfterParent(EventArgs e)
    {
        ((EventHandler?)Events[s_dpiChangedAfterParentEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseHover"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseHover(EventArgs e)
    {
        ((EventHandler?)Events[s_mouseHoverEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseMove"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseMove(MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[s_mouseMoveEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseUp"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseUp(MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[s_mouseUpEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="MouseWheel"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMouseWheel(MouseEventArgs e)
    {
        ((MouseEventHandler?)Events[s_mouseWheelEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="Move"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnMove(EventArgs e)
    {
        ((EventHandler?)Events[s_moveEvent])?.Invoke(this, e);

        if (RenderTransparent)
        {
            Invalidate();
        }
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onPaint to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnPaint(PaintEventArgs e)
    {
        ((PaintEventHandler?)Events[s_paintEvent])?.Invoke(this, e);
    }

    protected virtual void OnPaddingChanged(EventArgs e)
    {
        if (GetStyle(ControlStyles.ResizeRedraw))
        {
            Invalidate();
        }

        ((EventHandler?)Events[s_paddingChangedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle the erase background request from windows. It is
    ///  not necessary to call base.OnPaintBackground.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnPaintBackground(PaintEventArgs pevent)
    {
        // We need the true client rectangle as clip rectangle causes problems on "Windows Classic" theme.
        PInvokeCore.GetClientRect(new HandleRef<HWND>(_window, InternalHandle), out RECT rect);
        PaintBackground(pevent, rect);
    }

    // Transparent control support
    private void OnParentInvalidated(InvalidateEventArgs e)
    {
        if (!RenderTransparent)
        {
            return;
        }

        if (IsHandleCreated)
        {
            // move invalid rect into child space
            Rectangle cliprect = e.InvalidRect;
            Point offs = Location;
            cliprect.Offset(-offs.X, -offs.Y);
            cliprect = Rectangle.Intersect(ClientRectangle, cliprect);

            // if we don't intersect at all, do nothing
            if (cliprect.IsEmpty)
            {
                return;
            }

            Invalidate(cliprect);
        }
    }

    /// <summary>
    ///  Inheriting classes should override this method to handle this event.
    ///  Call base.onQueryContinueDrag to send this event to any registered event listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent)
    {
        ((QueryContinueDragEventHandler?)Events[s_queryContinueDragEvent])?.Invoke(this, qcdevent);
    }

    /// <summary>
    ///  Raises the <see cref="RegionChanged"/> event when the Region property has changed.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnRegionChanged(EventArgs e)
    {
        if (Events[s_regionChangedEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Raises the <see cref="Resize"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnResize(EventArgs e)
    {
        if ((_controlStyle & ControlStyles.ResizeRedraw) == ControlStyles.ResizeRedraw
            || GetState(States.ExceptionWhilePainting))
        {
            Invalidate();
        }

        LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds);
        ((EventHandler?)Events[s_resizeEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="PreviewKeyDown"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
    {
        ((PreviewKeyDownEventHandler?)Events[s_previewKeyDownEvent])?.Invoke(this, e);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnSizeChanged(EventArgs e)
    {
        OnResize(EventArgs.Empty);

        if (Events[s_sizeEvent] is EventHandler eh)
        {
            eh(this, e);
        }
    }

    /// <summary>
    ///  Raises the <see cref="ChangeUICues"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnChangeUICues(UICuesEventArgs e)
    {
        ((UICuesEventHandler?)Events[s_changeUICuesEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="OnStyleChanged"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnStyleChanged(EventArgs e)
    {
        ((EventHandler?)Events[s_styleChangedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="SystemColorsChanged"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnSystemColorsChanged(EventArgs e)
    {
        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
                children[i].OnSystemColorsChanged(EventArgs.Empty);
            }
        }

        Invalidate();

        ((EventHandler?)Events[s_systemColorsChangedEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="Validating"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void OnValidating(CancelEventArgs e)
    {
        ((CancelEventHandler?)Events[s_validatingEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the <see cref="Validated"/> event.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    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.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is an opportunity to rescale any constant sizes, glyphs or bitmaps before re-painting.
    ///  </para>
    /// </remarks>
    /// <param name="deviceDpiOld">The DPI value prior to the change.</param>
    /// <param name="deviceDpiNew">The DPI value after the change.</param>
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void RescaleConstantsForDpi(int deviceDpiOld, int deviceDpiNew)
    {
    }

    // This is basically OnPaintBackground, put in a separate method for ButtonBase,
    // which does all painting under OnPaint, and tries very hard to avoid double-painting the border pixels.
    internal void PaintBackground(PaintEventArgs e, Rectangle rectangle) =>
        PaintBackground(e, rectangle, BackColor, Point.Empty);

    internal void PaintBackground(PaintEventArgs e, Rectangle rectangle, Color backColor, Point scrollOffset = default)
    {
        ArgumentNullException.ThrowIfNull(e);

        bool renderColorTransparent = RenderColorTransparent(backColor);
        if (renderColorTransparent)
        {
            PaintTransparentBackground(e, rectangle);
        }

        // If the form or mdiclient is mirrored then we do not render the background image due to GDI+ issues.
        bool formRTL = ((this is Form || this is MdiClient) && IsMirrored);

        // The rest of this won't do much if BackColor is transparent and there is no BackgroundImage,
        // but we need to call it in the partial alpha case.

        if (BackgroundImage is not null && !DisplayInformation.HighContrast && !formRTL)
        {
            bool imageIsTransparent = ControlPaint.IsImageTransparent(BackgroundImage);
            if (!renderColorTransparent && BackgroundImageLayout == ImageLayout.Tile && imageIsTransparent)
            {
                PaintTransparentBackground(e, rectangle);
            }

            Point scrollLocation = scrollOffset;
            if (this is ScrollableControl scrollControl && scrollLocation != Point.Empty)
            {
                scrollLocation = scrollControl.AutoScrollPosition;
            }

            if (imageIsTransparent)
            {
                PaintBackColor(e, rectangle, backColor);
            }

            ControlPaint.DrawBackgroundImage(
                e.GraphicsInternal,
                BackgroundImage,
                backColor,
                BackgroundImageLayout,
                ClientRectangle,
                rectangle,
                scrollLocation,
                RightToLeft);
        }
        else
        {
            PaintBackColor(e, rectangle, backColor);
        }
    }

    private static void PaintBackColor(PaintEventArgs e, Rectangle rectangle, Color backColor)
    {
        // Common case of just painting the background. For this, we
        // use GDI because it is faster for simple things than creating
        // a graphics object, brush, etc. Also, we may be able to
        // use a system brush, avoiding the brush create altogether.

        Color color = backColor;

        // Note: PaintEvent.HDC == 0 if GDI+ has used the HDC -- it wouldn't be safe for us
        // to use it without enough bookkeeping to negate any performance gain of using GDI.
        if (!color.HasTransparency())
        {
            using DeviceContextHdcScope hdc = new(e);
            using CreateBrushScope hbrush = new(hdc.FindNearestColor(color));
            hdc.FillRectangle(rectangle, hbrush);
        }
        else if (!color.IsFullyTransparent())
        {
            // Color has some transparency (but not completely transparent) use GDI+.
            using var brush = color.GetCachedSolidBrushScope();
            e.Graphics.FillRectangle(brush, rectangle);
        }
    }

    // Paints a red rectangle with a red X, painted on a white background
    private void PaintException(PaintEventArgs e)
    {
        // As this is unusual we won't cache the pen.
        using Pen pen = new(Color.Red, width: 2);
        Rectangle clientRectangle = ClientRectangle;
        Rectangle rectangle = clientRectangle;
        rectangle.X++;
        rectangle.Y++;
        rectangle.Width--;
        rectangle.Height--;

        e.Graphics.DrawRectangle(pen, rectangle.X, rectangle.Y, rectangle.Width - 1, rectangle.Height - 1);
        rectangle.Inflate(-1, -1);
        e.Graphics.FillRectangle(Brushes.White, rectangle);
        e.Graphics.DrawLine(pen, clientRectangle.Left, clientRectangle.Top,
                            clientRectangle.Right, clientRectangle.Bottom);
        e.Graphics.DrawLine(pen, clientRectangle.Left, clientRectangle.Bottom,
                            clientRectangle.Right, clientRectangle.Top);
    }

    /// <summary>
    ///  Trick our parent into painting our background for us, or paint some default color if that doesn't work.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is the hardest part of implementing transparent controls; call this in
    ///   <see cref="OnPaintBackground(PaintEventArgs)"/>.
    ///  </para>
    /// </remarks>
    /// <param name="rectangle">The area to redraw.</param>
    /// <param name="transparentRegion">
    ///  Region of the rectangle to be transparent, or null for the entire control.
    /// </param>
    internal unsafe void PaintTransparentBackground(PaintEventArgs e, Rectangle rectangle, Region? transparentRegion = null)
    {
        Control? parent = ParentInternal;

        if (parent is null)
        {
            // For whatever reason, our parent can't paint our background, but we need some kind of background
            // since we're transparent.
            using DeviceContextHdcScope hdcNoParent = new(e);
            using CreateBrushScope hbrush = new(SystemColors.Control);
            hdcNoParent.FillRectangle(rectangle, hbrush);
            return;
        }

        // We need to use theming painting for certain controls (like TabPage) when they parent other controls.
        // But we don't want to to this always as this causes serious performance (at Runtime and DesignTime)
        // so checking for RenderTransparencyWithVisualStyles which is TRUE for TabPage and false by default.
        if (Application.RenderWithVisualStyles && parent.RenderTransparencyWithVisualStyles)
        {
            // When we are rendering with visual styles, we can use the cool DrawThemeParentBackground function
            // that UxTheme provides to render the parent's background. This function is control agnostic, so
            // we use the wrapper in ButtonRenderer - this should do the right thing for all controls,
            // not just Buttons.

            if (transparentRegion is not null)
            {
                Graphics g = e.GraphicsInternal;
                using GraphicsStateScope saveState = new(g);
                g.Clip = transparentRegion;
                ButtonRenderer.DrawParentBackground(g, rectangle, this);
            }
            else
            {
                ButtonRenderer.DrawParentBackground(e, rectangle, this);
            }

            return;
        }

        // Move the rendering area and setup it's size (we want to translate it to the parent's origin).
        Rectangle shift = new(-Left, -Top, parent.Width, parent.Height);

        // Moving the clipping rectangle to the parent coordinate system.
        Rectangle newClipRect = new(
            rectangle.Left + Left,
            rectangle.Top + Top,
            rectangle.Width,
            rectangle.Height);

        using DeviceContextHdcScope hdc = new(e);
        using SaveDcScope savedc = new(hdc);

        PInvokeCore.OffsetViewportOrgEx(hdc, -Left, -Top, lppt: null);

        using PaintEventArgs newArgs = new(hdc, newClipRect);

        if (transparentRegion is not null)
        {
            using GraphicsStateScope saveState = new(newArgs.Graphics);

            // Is this clipping something we can apply directly to the HDC?
            newArgs.Graphics.Clip = transparentRegion;
            newArgs.Graphics.TranslateClip(-shift.X, -shift.Y);
            InvokePaintBackground(parent, newArgs);
            InvokePaint(parent, newArgs);
        }
        else
        {
            InvokePaintBackground(parent, newArgs);
            InvokePaint(parent, newArgs);
        }
    }

    private void PaintWithErrorHandling(PaintEventArgs e, short layer)
    {
        try
        {
            CacheTextInternal = true;
            if (GetState(States.ExceptionWhilePainting))
            {
                if (layer == PaintLayerBackground)
                {
                    PaintException(e);
                }

                return;
            }

            try
            {
                switch (layer)
                {
                    case PaintLayerForeground:
                        OnPaint(e);
                        break;
                    case PaintLayerBackground:
                        if (!GetStyle(ControlStyles.Opaque))
                        {
                            OnPaintBackground(e);
                        }

                        break;
                    default:
                        Debug.Fail($"Unknown PaintLayer {layer}");
                        break;
                }
            }
            catch
            {
                // Exceptions during painting are nasty, because paint events happen so often.
                // So if user painting code has an issue, we make sure never to call it again,
                // so as not to spam the end-user with exception dialogs.

                SetState(States.ExceptionWhilePainting, true);
                Invalidate();

                throw;
            }
        }
        finally
        {
            CacheTextInternal = false;
        }
    }

    /// <summary>
    ///  Find ContainerControl that is the container of this control.
    /// </summary>
    internal ContainerControl? ParentContainerControl
    {
        get
        {
            for (Control? c = ParentInternal; c is not null; c = c.ParentInternal)
            {
                if (c is ContainerControl)
                {
                    return c as ContainerControl;
                }
            }

            return null;
        }
    }

    /// <summary>
    ///  Forces the control to apply layout logic to all of the child controls.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public void PerformLayout()
    {
        if (_cachedLayoutEventArgs is not null)
        {
            PerformLayout(_cachedLayoutEventArgs);
            _cachedLayoutEventArgs = null;

            // we need to be careful
            // about which LayoutEventArgs are used in
            // SuspendLayout, PerformLayout, ResumeLayout() sequences.
            SetExtendedState(ExtendedStates.ClearLayoutArgs, false);
        }
        else
        {
            PerformLayout(null, null);
        }
    }

    /// <summary>
    ///  Forces the control to apply layout logic to all of the child controls.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public void PerformLayout(Control? affectedControl, string? affectedProperty)
    {
        PerformLayout(new LayoutEventArgs(affectedControl, affectedProperty));
    }

    internal void PerformLayout(LayoutEventArgs args)
    {
        Debug.Assert(args is not null, "This method should never be called with null args.");
        if (GetAnyDisposingInHierarchy())
        {
            return;
        }

        if (LayoutSuspendCount > 0)
        {
            SetState(States.LayoutDeferred, true);
            if (_cachedLayoutEventArgs is null || GetExtendedState(ExtendedStates.ClearLayoutArgs))
            {
                _cachedLayoutEventArgs = args;
                if (GetExtendedState(ExtendedStates.ClearLayoutArgs))
                {
                    SetExtendedState(ExtendedStates.ClearLayoutArgs, false);
                }
            }

            LayoutEngine.ProcessSuspendedLayoutEventArgs(this, args);

            return;
        }

        // (Essentially the same as suspending layout while we layout, but we clear differently below.)
        LayoutSuspendCount = 1;

        try
        {
            CacheTextInternal = true;
            OnLayout(args);
        }
        finally
        {
            CacheTextInternal = false;
            // Rather than resume layout (which will could allow a deferred layout to layout the
            // the container we just finished laying out) we set layoutSuspendCount back to zero
            // and clear the deferred and dirty flags.
            SetState(States.LayoutDeferred | States.LayoutIsDirty, false);
            LayoutSuspendCount = 0;

            // LayoutEngine.Layout can return true to request that our parent resize us because
            // we did not have enough room for our contents. Now that we are unsuspended,
            // see if this happened and layout parent if necessary. (See also OnLayout)
            if (ParentInternal is not null && ParentInternal.GetState(States.LayoutIsDirty))
            {
                LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.PreferredSize);
            }
        }
    }

    /// <summary>
    ///  Performs data validation (not paint validation!) on a single control.
    ///
    ///  Returns whether validation failed:
    ///  False = Validation succeeded, control is valid, accept its new value
    ///  True = Validation was cancelled, control is invalid, reject its new value
    ///
    ///  NOTE: This is the lowest possible level of validation. It does not account
    ///  for the context in which the validation is occurring, eg. change of focus
    ///  between controls in a container. Stuff like that is handled by the caller.
    /// </summary>
    internal bool PerformControlValidation(bool bulkValidation)
    {
        // Skip validation for controls that don't support it
        if (!CausesValidation)
        {
            return false;
        }

        // Raise the 'Validating' event. Stop now if handler cancels (ie. control is invalid).
        // NOTE: Handler may throw an exception here, but we must not attempt to catch it.
        if (NotifyValidating())
        {
            return true;
        }

        // Raise the 'Validated' event. Handlers may throw exceptions here too - but
        // convert these to ThreadException events, unless the app is being debugged,
        // or the control is being validated as part of a bulk validation operation.
        if (bulkValidation || NativeWindow.WndProcShouldBeDebuggable)
        {
            NotifyValidated();
        }
        else
        {
            try
            {
                NotifyValidated();
            }
            catch (Exception e)
            {
                Application.OnThreadException(e);
            }
        }

        return false;
    }

    /// <summary>
    ///  Validates all the child controls in a container control. Exactly which controls are
    ///  validated and which controls are skipped is determined by <paramref name="validationConstraints"/>.
    ///  Return value indicates whether validation failed for any of the controls validated.
    ///  Calling function is responsible for checking the correctness of the validationConstraints argument.
    /// </summary>
    internal bool PerformContainerValidation(ValidationConstraints validationConstraints)
    {
        bool failed = false;

        // For every child control of this container control...
        foreach (Control c in Controls)
        {
            // First, if the control is a container, recurse into its descendants.
            if ((validationConstraints & ValidationConstraints.ImmediateChildren) != ValidationConstraints.ImmediateChildren
                && c.ShouldPerformContainerValidation()
                && c.PerformContainerValidation(validationConstraints))
            {
                failed = true;
            }

            // Next, use input flags to decide whether to validate the control itself
            if (((validationConstraints & ValidationConstraints.Selectable) == ValidationConstraints.Selectable && !c.GetStyle(ControlStyles.Selectable))
                || ((validationConstraints & ValidationConstraints.Enabled) == ValidationConstraints.Enabled && !c.Enabled)
                || ((validationConstraints & ValidationConstraints.Visible) == ValidationConstraints.Visible && !c.Visible)
                || ((validationConstraints & ValidationConstraints.TabStop) == ValidationConstraints.TabStop && !c.TabStop))
            {
                continue;
            }

            // Finally, perform validation on the control itself
            if (c.PerformControlValidation(true))
            {
                failed = true;
            }
        }

        return failed;
    }

    /// <summary>
    ///  Computes the location of the screen point p in client coordinates.
    /// </summary>
    public Point PointToClient(Point p)
    {
        PInvokeCore.MapWindowPoints((HWND)default, this, ref p);
        return p;
    }

    /// <summary>
    ///  Computes the location of the client point p in screen coordinates.
    /// </summary>
    public Point PointToScreen(Point p)
    {
        PInvokeCore.MapWindowPoints(this, (HWND)default, ref p);
        return p;
    }

    /// <summary>
    ///  This method is called by the application's message loop to pre-process input messages before they
    ///  are dispatched. If this method processes the message it must return true, in which case the message
    ///  loop will not dispatch the message.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   The messages that this method handles are WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR.
    ///  </para>
    ///  <para>
    ///   For WM_KEYDOWN and WM_SYSKEYDOWN messages, this first calls <see cref="ProcessCmdKey(ref Message, Keys)"/>
    ///   to check for command keys such as accelerators and menu shortcuts. If it doesn't process the message, then
    ///   <see cref="IsInputKey(Keys)"/> is called to check whether the key message represents an input key for the
    ///   control. Finally, if <see cref="IsInputKey(Keys)"/> indicates that the control isn't interested in the key
    ///   message, then <see cref="ProcessDialogKey(Keys)"/> is called to check for dialog keys such as TAB, arrow
    ///   keys, and mnemonics.
    ///  </para>
    ///  <para>
    ///   For WM_CHAR messages, <see cref="IsInputChar(char)"/> is first called to check whether the character
    ///   message represents an input character for the control. If <see cref="IsInputChar(char)"/> indicates that
    ///   the control isn't interested in the character message, then <see cref="ProcessDialogChar(char)"/> is
    ///   called to check for dialog characters such as mnemonics.
    ///  </para>
    ///  <para>
    ///   For WM_SYSCHAR messages, this calls <see cref="ProcessDialogChar(char)"/> to check for dialog characters
    ///   such as mnemonics.
    ///  </para>
    ///  <para>
    ///   When overriding this method, a control should return true to indicate that it has processed the message.
    ///   For messages that aren't  processed by the control, the result of "base.PreProcessMessage()" should be
    ///   returned.
    ///  </para>
    ///  <para>
    ///   Controls will typically override one of the more specialized methods (<see cref="IsInputChar(char)"/>,
    ///   <see cref="IsInputKey(Keys)"/>, <see cref="ProcessCmdKey(ref Message, Keys)"/>, <see cref="ProcessDialogChar(char)"/>,
    ///   or <see cref="ProcessDialogKey(Keys)"/>) instead of overriding this method.
    ///  </para>
    /// </remarks>
    public virtual bool PreProcessMessage(ref Message msg)
    {
        bool result;

        if (msg.MsgInternal == PInvokeCore.WM_KEYDOWN || msg.MsgInternal == PInvokeCore.WM_SYSKEYDOWN)
        {
            if (!GetExtendedState(ExtendedStates.UiCues))
            {
                ProcessUICues(ref msg);
            }

            Keys keyData = (Keys)(nint)msg.WParamInternal | ModifierKeys;
            if (ProcessCmdKey(ref msg, keyData))
            {
                result = true;
            }
            else if (IsInputKey(keyData))
            {
                SetExtendedState(ExtendedStates.InputKey, true);
                result = false;
            }
            else
            {
                result = ProcessDialogKey(keyData);
            }
        }
        else if (msg.MsgInternal == PInvokeCore.WM_CHAR || msg.MsgInternal == PInvokeCore.WM_SYSCHAR)
        {
            if (msg.MsgInternal == PInvokeCore.WM_CHAR && IsInputChar((char)(nint)msg.WParamInternal))
            {
                SetExtendedState(ExtendedStates.InputChar, true);
                result = false;
            }
            else
            {
                result = ProcessDialogChar((char)(nint)msg.WParamInternal);
            }
        }
        else
        {
            result = false;
        }

        return result;
    }

    /// <summary>
    ///  <see cref="PreProcessControlMessage(ref Message)"/> calls <see cref="PreProcessMessage(ref Message)"/>
    ///  on the <see cref="Control"/> referenced by the <paramref name="msg"/> <see cref="Message.HWnd"/>. It
    ///  handles dispatching of <see cref="OnPreviewKeyDown(PreviewKeyDownEventArgs)"/> and determines whether
    ///  to forward input messages when <see cref="PreProcessMessage(ref Message)"/> indicates it did not handle
    ///  the message by returning false.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This is the method that is called directly by the <see cref="Application"/>'s message loop.
    ///   See <see cref="Application.ThreadContext.PreTranslateMessage(ref MSG)"/>.
    ///  </para>
    /// </remarks>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public PreProcessControlState PreProcessControlMessage(ref Message msg)
        => PreProcessControlMessageInternal(target: null, ref msg);

    internal static PreProcessControlState PreProcessControlMessageInternal(Control? target, ref Message message)
    {
        target ??= FromChildHandle(message.HWnd);

        if (target is null)
        {
            return PreProcessControlState.MessageNotNeeded;
        }

        // Reset state that is used to make sure IsInputChar, IsInputKey and ProcessUICues are not called multiple times.
        target.SetExtendedState(ExtendedStates.InputKey, false);
        target.SetExtendedState(ExtendedStates.InputChar, false);
        target.SetExtendedState(ExtendedStates.UiCues, true);

        try
        {
            Keys keyData = (Keys)(nint)message.WParamInternal | ModifierKeys;

            // Allow control to preview key down message.
            if (message.Msg is ((int)PInvokeCore.WM_KEYDOWN) or ((int)PInvokeCore.WM_SYSKEYDOWN))
            {
                target.ProcessUICues(ref message);

                PreviewKeyDownEventArgs args = new(keyData);
                target.OnPreviewKeyDown(args);

                if (args.IsInputKey)
                {
                    // Control wants this message - indicate it should be dispatched.
                    return PreProcessControlState.MessageNeeded;
                }
            }

            PreProcessControlState state = PreProcessControlState.MessageNotNeeded;

            if (!target.PreProcessMessage(ref message))
            {
                if (message.MsgInternal == PInvokeCore.WM_KEYDOWN || message.MsgInternal == PInvokeCore.WM_SYSKEYDOWN)
                {
                    // Check if IsInputKey has already processed this message
                    // or if it is safe to call - we only want it to be called once.
                    if (target.GetExtendedState(ExtendedStates.InputKey) || target.IsInputKey(keyData))
                    {
                        state = PreProcessControlState.MessageNeeded;
                    }
                }
                else if (message.MsgInternal == PInvokeCore.WM_CHAR || message.MsgInternal == PInvokeCore.WM_SYSCHAR)
                {
                    // Check if IsInputChar has already processed this message
                    // or if it is safe to call - we only want it to be called once.
                    if (target.GetExtendedState(ExtendedStates.InputChar) || target.IsInputChar((char)(nint)message.WParamInternal))
                    {
                        state = PreProcessControlState.MessageNeeded;
                    }
                }
            }
            else
            {
                state = PreProcessControlState.MessageProcessed;
            }

            return state;
        }
        finally
        {
            target.SetExtendedState(ExtendedStates.UiCues, false);
        }
    }

    /// <summary>
    ///  Processes a command key.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called during message pre-processing to handle command keys. Command keys are keys that always
    ///   take precedence over regular input keys. Examples of command keys include accelerators and menu shortcuts. The
    ///   method must return <see langword="true"/> to indicate that it has  processed the command key, or
    ///   <see langword="false"/> to indicate that the key is not a command key.
    ///  </para>
    ///  <para>
    ///   If the control has a parent, the key is passed to the parent's <see cref="ProcessCmdKey(ref Message, Keys)"/>
    ///   method. The net effect is that command keys are "bubbled" up the control hierarchy. In addition to the key the
    ///   user pressed, the key data also indicates which, if any, modifier keys were pressed at the same time as the
    ///   key. Modifier keys include the SHIFT, CTRL, and ALT keys.
    ///  </para>
    /// </remarks>
    protected virtual bool ProcessCmdKey(ref Message msg, Keys keyData) =>
        _parent?.ProcessCmdKey(ref msg, keyData) ?? false;

    private unsafe void PrintToMetaFile(HDC hDC, IntPtr lParam)
    {
        Debug.Assert((OBJ_TYPE)PInvokeCore.GetObjectType(hDC) == OBJ_TYPE.OBJ_ENHMETADC,
            "PrintToMetaFile() called with a non-Enhanced MetaFile DC.");
        Debug.Assert((lParam & (long)PInvoke.PRF_CHILDREN) != 0,
            "PrintToMetaFile() called without PRF_CHILDREN.");

        // Strip the PRF_CHILDREN flag. We will manually walk our children and print them.
        lParam = (nint)(lParam & (long)~PInvoke.PRF_CHILDREN);

        // We're the root control, so we need to set up our clipping region. Retrieve the
        // x-coordinates and y-coordinates of the viewport origin for the specified device context.
        Point viewportOrg = default;
        bool success = PInvokeCore.GetViewportOrgEx(hDC, &viewportOrg);
        Debug.Assert(success, "GetViewportOrgEx() failed.");

        using RegionScope hClippingRegion = new(
            viewportOrg.X,
            viewportOrg.Y,
            viewportOrg.X + Width,
            viewportOrg.Y + Height);

        Debug.Assert(!hClippingRegion.IsNull, "CreateRectRgn() failed.");

        // Select the new clipping region; make sure it's a SIMPLEREGION or NULLREGION
        GDI_REGION_TYPE selectResult = PInvokeCore.SelectClipRgn(hDC, hClippingRegion);
        Debug.Assert(
            selectResult is GDI_REGION_TYPE.SIMPLEREGION or GDI_REGION_TYPE.NULLREGION,
            "SIMPLEREGION or NULLLREGION expected.");

        PrintToMetaFileRecursive(hDC, lParam, new Rectangle(Point.Empty, Size));
    }

    private protected virtual void PrintToMetaFileRecursive(HDC hDC, IntPtr lParam, Rectangle bounds)
    {
        // We assume the target does not want us to offset the root control in the metafile.

        using DCMapping mapping = new(hDC, bounds);

        // Print the non-client area.
        PrintToMetaFile_SendPrintMessage(hDC, (nint)(lParam & (long)~PInvoke.PRF_CLIENT));

        // Figure out mapping for the client area.
        bool success = PInvokeCore.GetWindowRect(this, out var windowRect);
        Debug.Assert(success, "GetWindowRect() failed.");
        Point clientOffset = PointToScreen(Point.Empty);
        clientOffset = new(clientOffset.X - windowRect.left, clientOffset.Y - windowRect.top);
        Rectangle clientBounds = new(clientOffset, ClientSize);

        using DCMapping clientMapping = new(hDC, clientBounds);

        // Print the client area.
        PrintToMetaFile_SendPrintMessage(hDC, (nint)(lParam & (long)~PInvoke.PRF_NONCLIENT));

        // Paint children in reverse Z-Order.
        int count = Controls.Count;
        for (int i = count - 1; i >= 0; i--)
        {
            Control child = Controls[i];
            if (child.Visible)
            {
                child.PrintToMetaFileRecursive(hDC, lParam, child.Bounds);
            }
        }
    }

    private void PrintToMetaFile_SendPrintMessage(HDC hDC, nint lParam)
    {
        if (GetStyle(ControlStyles.UserPaint))
        {
            // We let user paint controls paint directly into the metafile
            PInvokeCore.SendMessage(this, PInvokeCore.WM_PRINT, (WPARAM)hDC, (LPARAM)lParam);
        }
        else
        {
            // If a system control has no children in the Controls collection we
            // restore the PRF_CHILDREN flag because it may internally
            // have nested children we do not know about. ComboBox is a
            // good example.
            if (Controls.Count == 0)
            {
                lParam |= PInvoke.PRF_CHILDREN;
            }

            // System controls must be painted into a temporary bitmap
            // which is then copied into the metafile. (Old GDI line drawing
            // is 1px thin, which causes borders to disappear, etc.)
            using MetafileDCWrapper dcWrapper = new(hDC, Size);
            PInvokeCore.SendMessage(this, PInvokeCore.WM_PRINT, (WPARAM)dcWrapper.HDC, (LPARAM)lParam);
        }
    }

    /// <summary>
    ///  Processes a dialog character.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called during message preprocessing to handle dialog characters, such as control mnemonics.
    ///   This method is called only if the <see cref="IsInputChar(char)"/> method indicates that the control is not
    ///   processing the character. The <see cref="ProcessDialogChar(char)"/> method simply sends the character to the
    ///   parent's <see cref="ProcessDialogChar(char)"/> method, or returns <see langword="false"/> if the control has no
    ///   parent. The <see cref="Form"/> class overrides this method to perform actual processing of dialog characters.
    ///  </para>
    /// </remarks>
    protected virtual bool ProcessDialogChar(char charCode) => _parent?.ProcessDialogChar(charCode) ?? false;

    /// <summary>
    ///  Processes a dialog key.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called during message preprocessing to handle dialog characters, such as TAB, RETURN, ESC, and
    ///   arrow keys. This method is called only if the <see cref="IsInputKey(Keys)"/> method indicates that the control
    ///   is not processing the key. The <see cref="ProcessDialogKey(Keys)"/> simply sends the character to the parent's
    ///   <see cref="ProcessDialogKey(Keys)"/> method, or returns <see langword="false"/> if the control has no parent.
    ///   The <see cref="Form"/> class overrides this method to perform actual processing of dialog keys.
    ///  </para>
    /// </remarks>
    protected virtual bool ProcessDialogKey(Keys keyData) => _parent?.ProcessDialogKey(keyData) ?? false;

    /// <summary>
    ///  Processes a key message and generates the appropriate control events.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called when a control receives a keyboard message. The method is responsible for generating the
    ///   appropriate key events for the message by calling the <see cref="OnKeyPress(KeyPressEventArgs)"/>,
    ///   <see cref="OnKeyDown(KeyEventArgs)"/>, or <see cref="OnKeyUp(KeyEventArgs)"/>. The <paramref name="m"/>
    ///   parameter contains the window message that must be processed. Possible values for the <see cref="Message.Msg"/>
    ///   property are WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP, and WM_IME_CHAR.
    ///  </para>
    /// </remarks>
    protected virtual bool ProcessKeyEventArgs(ref Message m)
    {
        KeyEventArgs? ke = null;
        KeyPressEventArgs? kpe = null;
        WPARAM newWParam = 0;

        if (m.MsgInternal == PInvokeCore.WM_CHAR || m.MsgInternal == PInvokeCore.WM_SYSCHAR)
        {
            int charsToIgnore = ImeWmCharsToIgnore;

            if (charsToIgnore > 0)
            {
                charsToIgnore--;
                ImeWmCharsToIgnore = charsToIgnore;
                return false;
            }
            else
            {
                kpe = new KeyPressEventArgs((char)(int)m.WParamInternal);
                OnKeyPress(kpe);
                newWParam = kpe.KeyChar;
            }
        }
        else if (m.MsgInternal == PInvokeCore.WM_IME_CHAR)
        {
            int charsToIgnore = ImeWmCharsToIgnore;

            charsToIgnore += (3 - sizeof(char));
            ImeWmCharsToIgnore = charsToIgnore;

            kpe = new KeyPressEventArgs((char)(int)m.WParamInternal);

            char preEventCharacter = kpe.KeyChar;
            OnKeyPress(kpe);

            // If the character wasn't changed, just use the original value rather than round tripping.
            newWParam = kpe.KeyChar == preEventCharacter ? m.WParamInternal : (WPARAM)kpe.KeyChar;
        }
        else
        {
            ke = new KeyEventArgs((Keys)(int)m.WParamInternal | ModifierKeys);
            if (m.MsgInternal == PInvokeCore.WM_KEYDOWN || m.MsgInternal == PInvokeCore.WM_SYSKEYDOWN)
            {
                OnKeyDown(ke);
            }
            else
            {
                OnKeyUp(ke);
            }
        }

        if (kpe is not null)
        {
            m.WParamInternal = newWParam;
            return kpe.Handled;
        }
        else
        {
            if (ke!.SuppressKeyPress)
            {
                RemovePendingMessages(PInvokeCore.WM_CHAR, PInvokeCore.WM_CHAR);
                RemovePendingMessages(PInvokeCore.WM_SYSCHAR, PInvokeCore.WM_SYSCHAR);
                RemovePendingMessages(PInvokeCore.WM_IME_CHAR, PInvokeCore.WM_IME_CHAR);
            }

            return ke.Handled;
        }
    }

    /// <summary>
    ///  Processes a key message.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called when a control receives a keyboard message. The method first determines whether the
    ///   control has a parent; if so, it calls the parent's <see cref="ProcessKeyPreview(ref Message)"/> method. If the
    ///   parent's <see cref="ProcessKeyPreview(ref Message)"/> method does not process the message then the
    ///   <see cref="ProcessKeyEventArgs(ref Message)"/> is called to generate the appropriate keyboard events. The
    ///   <paramref name="m"/> parameter contains the window message that must be processed. Possible values for the
    ///   <see cref="Message.Msg"/> property are WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and WM_SYSKEYUP.
    ///  </para>
    /// </remarks>
    protected internal virtual bool ProcessKeyMessage(ref Message m) =>
        (_parent is not null && _parent.ProcessKeyPreview(ref m)) || ProcessKeyEventArgs(ref m);

    /// <summary>
    ///  Previews a keyboard message.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called by a child control when the child control receives a keyboard message. The child control
    ///   calls this method before generating any keyboard events for the message. If this method returns <see langword="true"/>,
    ///   the child control considers the message processed and does not generate any keyboard events. The
    ///   <paramref name="m"/> parameter contains the window message to preview. Possible values for the
    ///   <see cref="Message.Msg"/> property are WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and WM_SYSKEYUP. The
    ///   <see cref="ProcessKeyPreview(ref Message)"/> method simply sends the character to the parent's
    ///   <see cref="ProcessKeyPreview(ref Message)"/> method, or returns <see langword="false"/> if the control has no
    ///   parent. The <see cref="Form"/> class overrides this method to perform actual processing of dialog keys.
    ///  </para>
    /// </remarks>
    protected virtual bool ProcessKeyPreview(ref Message m) => _parent?.ProcessKeyPreview(ref m) ?? false;

    /// <summary>
    ///  Processes a mnemonic character.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   This method is called to give a control the opportunity to process a mnemonic character. The method should
    ///   check if the control is in a state to process mnemonics and if the given  character represents a mnemonic. If
    ///   so, the method should perform the action associated with the mnemonic and return <see langword="true"/>.
    ///   If not, the method should return <see langword="false"/>. Implementations of this method often use the
    ///   <see cref="IsMnemonic(char, string?)"/> method to determine whether the given character matches a mnemonic
    ///   in the control's text.
    ///  </para>
    /// </remarks>
    protected internal virtual bool ProcessMnemonic(char charCode) => false;

    /// <summary>
    ///  Preprocess keys which affect focus indicators and keyboard cues.
    /// </summary>
    internal void ProcessUICues(ref Message msg)
    {
        Keys keyCode = (Keys)(nint)msg.WParamInternal & Keys.KeyCode;

        if (keyCode is not Keys.F10 and not Keys.Menu and not Keys.Tab)
        {
            return;  // PERF: don't WM_QUERYUISTATE if we don't have to.
        }

        Control? topMostParent = null;
        uint current = (uint)PInvokeCore.SendMessage(this, PInvokeCore.WM_QUERYUISTATE);

        // don't trust when a control says the accelerators are showing.
        // make sure the topmost parent agrees with this as we could be in a mismatched state.
        if (current == 0 /*accelerator and focus cues are showing*/)
        {
            topMostParent = TopMostParent;
            current = (uint)PInvokeCore.SendMessage(topMostParent, PInvokeCore.WM_QUERYUISTATE);
        }

        uint toClear = 0;

        // if we are here, a key or tab has been pressed on this control.
        // now that we know the state of accelerators, check to see if we need
        // to show them. NOTE: due to the strangeness of the API we OR in
        // the opposite of what we want to do. So if we want to show accelerators,
        // we OR in UISF_HIDEACCEL, then call UIS_CLEAR to clear the "hidden" state.

        if (keyCode is Keys.F10 or Keys.Menu)
        {
            if ((current & PInvoke.UISF_HIDEACCEL) != 0)
            {
                // Keyboard accelerators are hidden, they need to be shown
                toClear |= PInvoke.UISF_HIDEACCEL;
            }
        }

        if (keyCode == Keys.Tab)
        {
            if ((current & PInvoke.UISF_HIDEFOCUS) != 0)
            {
                // Focus indicators are hidden, they need to be shown
                toClear |= PInvoke.UISF_HIDEFOCUS;
            }
        }

        if (toClear != 0)
        {
            // We've detected some state we need to unset, usually clearing the hidden state of
            // the accelerators. We need to get the topmost parent and call CHANGEUISTATE so
            // that the entire tree of controls is
            topMostParent ??= TopMostParent;

            // A) if we're parented to a native dialog - REFRESH our child states ONLY
            //       Then we've got to send a WM_UPDATEUISTATE to the topmost managed control (which will be non-toplevel)
            //           (we assume here the root native window has changed UI state, and we're not to manage the UI state for it)
            //
            // B) if we're totally managed - CHANGE the root window state AND REFRESH our child states.
            //       Then we've got to send a WM_CHANGEUISTATE to the topmost managed control (which will be toplevel)
            //       According to MSDN, WM_CHANGEUISTATE will generate WM_UPDATEUISTATE messages for all immediate children (via DefWndProc)
            //           (we're in charge here, we've got to change the state of the root window)
            PInvokeCore.SendMessage(
                topMostParent,
                PInvoke.GetParent(topMostParent).IsNull ? PInvokeCore.WM_CHANGEUISTATE : PInvokeCore.WM_UPDATEUISTATE,
                (WPARAM)((int)PInvoke.UIS_CLEAR | ((int)toClear << 16)));
        }
    }

    /// <summary>
    ///  Raises the event associated with key with the event data of
    ///  e and a sender of this control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void RaiseDragEvent(object key, DragEventArgs e)
    {
        ((DragEventHandler?)Events[key])?.Invoke(this, e);
    }

    /// <summary>
    ///  Raises the event associated with <paramref name="key"/> with the event data of <paramref name="e"/>
    ///  and a sender of this control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void RaisePaintEvent(object key, PaintEventArgs e)
    {
        ((PaintEventHandler?)Events[s_paintEvent])?.Invoke(this, e);
    }

    private void RemovePendingMessages(MessageId msgMin, MessageId msgMax)
    {
        if (!IsDisposed)
        {
            MSG msg = default;
            while (PInvokeCore.PeekMessage(&msg, this, (uint)msgMin, (uint)msgMax, PEEK_MESSAGE_REMOVE_TYPE.PM_REMOVE))
            {
                // No-op.
            }
        }
    }

    /// <summary>
    ///  Resets the back color to be based on the parent's back color.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual void ResetBackColor()
    {
        BackColor = Color.Empty;
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual void ResetCursor()
    {
        Cursor = null;
    }

    private void ResetEnabled()
    {
        Enabled = true;
    }

    /// <summary>
    ///  Resets the font to be based on the parent's font.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual void ResetFont()
    {
        Font = null;
    }

    /// <summary>
    ///  Resets the fore color to be based on the parent's fore color.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual void ResetForeColor()
    {
        ForeColor = Color.Empty;
    }

    private void ResetLocation()
    {
        Location = new Point(0, 0);
    }

    private void ResetMargin()
    {
        Margin = DefaultMargin;
    }

    private void ResetMinimumSize()
    {
        MinimumSize = DefaultMinimumSize;
    }

    private void ResetPadding()
    {
        CommonProperties.ResetPadding(this);
    }

    private void ResetSize()
    {
        Size = DefaultSize;
    }

    /// <summary>
    ///  Resets the RightToLeft to be the default.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public virtual void ResetRightToLeft()
    {
        RightToLeft = RightToLeft.Inherit;
    }

    /// <summary>
    ///  Forces the recreation of the handle for this control. Inheriting controls
    ///  must call base.RecreateHandle.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void RecreateHandle()
    {
        RecreateHandleCore();
    }

    internal virtual void RecreateHandleCore()
    {
        lock (this)
        {
            if (!IsHandleCreated)
            {
                // Do nothing if the handle is not created yet.
                return;
            }

            bool focused = ContainsFocus;

            Debug.WriteLineIf(CoreSwitches.PerfTrack.Enabled, $"RecreateHandle: {GetType().FullName} [Text={Text}]");

            bool created = GetState(States.Created);
            if (GetState(States.TrackingMouseEvent))
            {
                SetState(States.MouseEnterPending, true);
                UnhookMouseEvent();
            }

            HWND parentHandle = PInvoke.GetParent(this);

            Control?[]? controlSnapshot = null;
            SetState(States.Recreate, true);

            try
            {
                // Inform child controls that their parent is recreating handle.

                // The default behavior is to now SetParent to parking window, then
                // SetParent back after the parent's handle has been recreated.
                // This behavior can be overridden in OnParentHandleRecreat* and is in ListView.

                if (ChildControls is { } children && children.Count > 0)
                {
                    controlSnapshot = new Control[children.Count];
                    for (int i = 0; i < children.Count; i++)
                    {
                        Control childControl = children[i];
                        if (childControl is not null && childControl.IsHandleCreated)
                        {
                            // SetParent to parking window
                            childControl.OnParentHandleRecreating();

                            // if we were successful, remember this control
                            // so we can raise OnParentHandleRecreated
                            controlSnapshot[i] = childControl;
                        }
                        else
                        {
                            // put in a null slot which we'll skip over later.
                            controlSnapshot[i] = null;
                        }
                    }
                }

                // do the main work of recreating the handle
                DestroyHandle();

                // Note that CreateHandle --> _window.CreateHandle may fail due to Dpi awareness setting.
                // By carefully choosing the correct parking window / keeping this and this.Parent Dpi awareness untouched,
                // the call shouldn't fail.
                // However, it could fail if this.CreateParams.Parent is changed outside our control.
                CreateHandle();
            }
            catch (Exception)
            {
                // this.DestroyHandle succeeded, but CreateHandle failed.
                // The control is actually destroyed.
                if (_window.Handle == IntPtr.Zero)
                {
                    SetState(States.Created, false);
                }

                throw;
            }
            finally
            {
                SetState(States.Recreate, false);

                // Inform children their parent's handle has been created.
                // This means
                // an Exception gets thrown before/during the invocation of DestroyHandle.
                //      In this case, GetState(States.Created) == true.
                //      We will restore the Parent value of the (visited) child controls.
                // - or -
                // an Exception gets thrown in CreateHandle.
                //      In this case, _window.Handle will be IntPtr.Zero,
                //      and we should have GetState(States.Created) == false.
                //      Do not go through this if CreateHandle fails (and an Exception is probably on its way bubbling up).
                // - or -
                // CreateHandle is successful.
                //      We will move the child controls to the new parent.
                if (controlSnapshot is not null && IsHandleCreated)
                {
                    for (int i = 0; i < controlSnapshot.Length; i++)
                    {
                        Control? childControl = controlSnapshot[i];
                        if (childControl is not null && childControl.IsHandleCreated)
                        {
                            // Re-parent the control.
                            // If the control fails to re-parent itself,
                            // It and its next siblings will keep States.ParentRecreating state,
                            // parked in ParkingWindow.
                            // We let the error bubble up immediately.
                            childControl.OnParentHandleRecreated();
                        }
                    }
                }
            }

            if (created)
            {
                CreateControl();
            }

            if (
                // The window has a parent Win32 window before re-creation
                !parentHandle.IsNull
                // But the parent is not a managed WinForms Control, or this.Parent is null
                && (FromHandle(parentHandle) is null || _parent is null)
                // Still, parentHandle is a valid native Win32 window handle, e.g. the desktop window.
                && PInvoke.IsWindow(parentHandle))
            {
                // correctly parent back up to where we were before.
                // if we were parented to a proper windows forms control, CreateControl would have properly parented
                // us back.
                if (PInvoke.SetParent(this, parentHandle) == IntPtr.Zero)
                {
                    // Somehow we failed to SetParent due to, e.g., different Dpi awareness setting.
                    throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed);
                }
            }

            // Restore control focus
            if (focused)
            {
                Focus();
            }

            GC.KeepAlive(this);
        }
    }

    /// <summary>
    ///  Computes the location of the screen rectangle r in client coordinates.
    /// </summary>
    public Rectangle RectangleToClient(Rectangle r)
    {
        RECT rect = r;
        PInvokeCore.MapWindowPoints(HWND.Null, this, ref rect);
        return rect;
    }

    /// <summary>
    ///  Computes the location of the client rectangle r in screen coordinates.
    /// </summary>
    public Rectangle RectangleToScreen(Rectangle r)
    {
        RECT rect = r;
        PInvokeCore.MapWindowPoints(this, HWND.Null, ref rect);
        return rect;
    }

    /// <summary>
    ///  Reflects the specified message to the control that is bound to the specified handle.
    /// </summary>
    /// <returns>
    ///  <see langword="true"/> if the message was reflected; otherwise, <see langword="false"/>.
    /// </returns>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected static bool ReflectMessage(IntPtr hWnd, ref Message m)
    {
        if (FromHandle(hWnd) is not { } control)
        {
            return false;
        }

        m.ResultInternal = PInvokeCore.SendMessage(
            control,
            MessageId.WM_REFLECT | m.MsgInternal,
            m.WParamInternal,
            m.LParamInternal);

        return true;
    }

    /// <summary>
    ///  Forces the control to invalidate and immediately repaint itself and any children.
    /// </summary>
    public virtual void Refresh()
    {
        Invalidate(invalidateChildren: true);
        Update();
    }

    /// <summary>
    ///  Releases UI Automation provider for specified window.
    /// </summary>
    /// <param name="handle">The window handle.</param>
    internal virtual void ReleaseUiaProvider(HWND handle)
    {
        if (!handle.IsNull)
        {
            // When a window that previously returned providers has been destroyed,
            // you should notify UI Automation by calling the UiaReturnRawElementProvider
            // as follows: UiaReturnRawElementProvider(hwnd, 0, 0, NULL). This call tells
            // UI Automation that it can safely remove all map entries that refer to the specified window.
            PInvoke.UiaReturnRawElementProvider(handle, 0, 0, (IRawElementProviderSimple*)null);
        }

        if (OsVersion.IsWindows8OrGreater() && TryGetAccessibilityObject(out AccessibleObject? accessibleObject))
        {
            PInvoke.UiaDisconnectProvider(accessibleObject, skipOSCheck: true);
        }

        Properties.RemoveValue(s_accessibilityProperty);
    }

    private protected bool TryGetAccessibilityObject([NotNullWhen(true)] out AccessibleObject? accessibleObject) =>
        Properties.TryGetValue(s_accessibilityProperty, out accessibleObject);

    /// <summary>
    ///  Resets the mouse leave listeners.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void ResetMouseEventArgs()
    {
        if (GetState(States.TrackingMouseEvent))
        {
            UnhookMouseEvent();
            HookMouseEvent();
        }
    }

    /// <summary>
    ///  Resets the text to it's default value.
    /// </summary>
    public virtual void ResetText()
    {
        Text = string.Empty;
    }

    private void ResetVisible()
    {
        Visible = true;
    }

    /// <summary>
    ///  Resumes normal layout logic. This will force a layout immediately
    ///  if there are any pending layout requests.
    /// </summary>
    public void ResumeLayout() => ResumeLayout(performLayout: true);

    /// <summary>
    ///  Resumes normal layout logic. If performLayout is set to true then
    ///  this will force a layout immediately if there are any pending layout requests.
    /// </summary>
    public void ResumeLayout(bool performLayout)
    {
        bool performedLayout = false;
        if (LayoutSuspendCount > 0)
        {
            if (LayoutSuspendCount == 1)
            {
                LayoutSuspendCount++;
                try
                {
                    OnLayoutResuming(performLayout);
                }
                finally
                {
                    LayoutSuspendCount--;
                }
            }

            LayoutSuspendCount--;
            if (LayoutSuspendCount == 0 && GetState(States.LayoutDeferred) && performLayout)
            {
                PerformLayout();
                performedLayout = true;
            }
        }

        if (!performedLayout)
        {
            SetExtendedState(ExtendedStates.ClearLayoutArgs, true);
        }

        // We've had this since Everett, but it seems wrong, redundant and a performance hit. The
        // correct layout calls are already made when bounds or parenting changes, which is all
        // we care about. We may want to call this at layout suspend count == 0, but certainly
        // not for all resumes. I  tried removing it, and doing it only when suspendCount == 0,
        // but we break things at every step.

        if (!performLayout)
        {
            CommonProperties.xClearPreferredSizeCache(this);

            if (ChildControls is { } children)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    Control child = children[i];
                    LayoutEngine.InitLayout(child, BoundsSpecified.All);
                    CommonProperties.xClearPreferredSizeCache(child);
                }
            }
        }
    }

    /// <summary>
    ///  Used to actually register the control as a drop target.
    /// </summary>
    internal void SetAcceptDrops(bool accept)
    {
        if (accept == GetState(States.DropTarget) || !IsHandleCreated)
        {
            return;
        }

        try
        {
            if (Application.OleRequired() != ApartmentState.STA)
            {
                throw new ThreadStateException(SR.ThreadMustBeSTA);
            }

            if (accept)
            {
                // Register
                HRESULT hr = PInvoke.RegisterDragDrop(this, new DropTarget(this));
                if (hr != HRESULT.S_OK && hr != HRESULT.DRAGDROP_E_ALREADYREGISTERED)
                {
                    throw Marshal.GetExceptionForHR((int)hr)!;
                }
            }
            else
            {
                // Revoke
                HRESULT hr = PInvoke.RevokeDragDrop(this);
                if (hr != HRESULT.S_OK && hr != HRESULT.DRAGDROP_E_NOTREGISTERED)
                {
                    throw Marshal.GetExceptionForHR((int)hr)!;
                }
            }

            SetState(States.DropTarget, accept);
        }
        catch (Exception e)
        {
            throw new InvalidOperationException(SR.DragDropRegFailed, e);
        }
    }

    /// <summary>
    ///  Scales to entire control and any child controls.
    /// </summary>
    [Obsolete("This method has been deprecated. Use the Scale(SizeF ratio) method instead. https://go.microsoft.com/fwlink/?linkid=14202")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void Scale(float ratio)
    {
        ScaleCore(ratio, ratio);
    }

    /// <summary>
    ///  Scales the entire control and any child controls.
    /// </summary>
    [Obsolete("This method has been deprecated. Use the Scale(SizeF ratio) method instead. https://go.microsoft.com/fwlink/?linkid=14202")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void Scale(float dx, float dy)
    {
        using SuspendLayoutScope scope = new(this);
        ScaleCore(dx, dy);
    }

    /// <summary>
    ///  Scales a control and its children given a scaling factor.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public void Scale(SizeF factor)
    {
        // Manually call ScaleControl recursively instead of the internal scale method
        // when someone calls this method, they really do want to do some sort of
        // zooming feature, as opposed to AutoScale.
        using (new LayoutTransaction(this, this, PropertyNames.Bounds, resumeLayout: false))
        {
            ScaleControl(factor, factor);
            if (ScaleChildren && ChildControls is { } children)
            {
                for (int i = 0; i < children.Count; i++)
                {
                    children[i].Scale(factor);
                }
            }
        }

        LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds);
    }

    /// <summary>
    ///  Scales control and its children given a pair of scaling factors.
    ///  IncludedFactor will be applied to the dimensions of controls based on
    ///  their <see cref="RequiredScaling"/> property. For example, if a control's
    ///  RequiredScaling property returns Width, the width of the control will
    ///  be scaled according to the includedFactor value.
    /// </summary>
    /// <param name="includedFactor">
    ///  Control bounds that are included in <see cref="RequiredScaling"/>.
    ///  If the factor is empty, it indicates that no scaling of those control dimensions should be done.
    /// </param>
    /// <param name="excludedFactor">
    ///  Control bounds that are not included in <see cref="RequiredScaling"/>.
    ///  If the factor is empty, it indicates that no scaling of those control dimensions should be done.
    /// </param>
    /// <param name="requestingControl">Control that has requested the scaling function.</param>
    /// <param name="causedByFontChanged">
    ///  Indicates if it need to update Window font for controls that need it, i.e. controls using default or
    ///  inherited font,that are also not user-painted.
    /// </param>
    internal virtual void Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, bool causedByFontChanged = false)
    {
        // When we scale, we are establishing new baselines for the
        // positions of all controls. Therefore, we should resume(false).
        using (new LayoutTransaction(this, this, PropertyNames.Bounds, false))
        {
            ScaleControl(includedFactor, excludedFactor);

            // Certain controls like 'PropertyGrid' does special scaling. Differing scaling to their own methods.
            if (!_doNotScaleChildren)
            {
                ScaleChildControls(includedFactor, excludedFactor, requestingControl, causedByFontChanged);
            }
        }

        LayoutTransaction.DoLayout(this, this, PropertyNames.Bounds);
    }

    /// <summary>
    ///  Scales control and its children given a pair of scaling factors.
    ///  IncludedFactor will be applied to the dimensions of controls based on
    ///  their <see cref="RequiredScaling"/> property. For example, if a control's
    ///  RequiredScaling property returns Width, the width of the control will
    ///  be scaled according to the includedFactor value.
    /// </summary>
    /// <param name="includedFactor">
    ///  Control bounds that are included in <see cref="RequiredScaling"/>.
    ///  If the factor is empty, it indicates that no scaling of those control dimensions should be done.
    /// </param>
    /// <param name="excludedFactor">
    ///  Control bounds that are not included in <see cref="RequiredScaling"/>.
    ///  If the factor is empty, it indicates that no scaling of those control dimensions should be done.
    /// </param>
    /// <param name="requestingControl">Control that has requested the scaling function.</param>
    /// <param name="causedByFontChanged">Indicates if it need to update Window font for controls
    ///  that need it, i.e. controls using default or inherited font, that are also not user-painted.</param>
    internal void ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, bool causedByFontChanged = false)
    {
        if (!ScaleChildren || ChildControls is not { } children)
        {
            return;
        }

        for (int i = 0; i < children.Count; i++)
        {
            Control child = children[i];

            // ContainerControls get their own OnFontChanged Events and scale.
            // If this scaling is caused by ResumeLayout instead of OnFontChanged,
            // We would be scaling all container controls.
            if (child is ContainerControl && causedByFontChanged)
            {
                continue;
            }

            // Update window font before scaling, as controls often use font metrics during scaling.
            if (causedByFontChanged)
            {
                if (ScaleHelper.IsScalingRequirementMet && !GetStyle(ControlStyles.UserPaint) && !IsFontSet())
                {
                    SetWindowFont();
                }
            }

            child.Scale(includedFactor, excludedFactor, requestingControl, causedByFontChanged);
        }
    }

    /// <summary>
    ///  Scales the children of this control. The default implementation walks the controls
    ///  collection for the control and calls Scale on each control.
    ///  IncludedFactor will be applied to the dimensions of controls based on
    ///  their RequiredScaling property. For example, if a control's
    ///  RequiredScaling property returns Width, the width of the control will
    ///  be scaled according to the includedFactor value.
    ///
    ///  The excludedFactor parameter is used to scale those control bounds who
    ///  are not included in RequiredScaling.
    ///
    ///  If a factor is empty, it indicates that no scaling of those control
    ///  dimensions should be done.
    ///
    ///  The requestingControl property indicates which control has requested
    ///  the scaling function.
    /// </summary>
    internal void ScaleControl(SizeF includedFactor, SizeF excludedFactor)
    {
        try
        {
            ScalingInProgress = true;

            BoundsSpecified includedSpecified = BoundsSpecified.None;
            BoundsSpecified excludedSpecified = BoundsSpecified.None;

            if (!includedFactor.IsEmpty)
            {
                includedSpecified = RequiredScaling;
            }

            if (!excludedFactor.IsEmpty)
            {
                excludedSpecified |= (~RequiredScaling & BoundsSpecified.All);
            }

            if (includedSpecified != BoundsSpecified.None)
            {
                ScaleControl(includedFactor, includedSpecified);
            }

            if (excludedSpecified != BoundsSpecified.None)
            {
                ScaleControl(excludedFactor, excludedSpecified);
            }

            if (!includedFactor.IsEmpty)
            {
                RequiredScaling = BoundsSpecified.None;
            }
        }
        finally
        {
            ScalingInProgress = false;
        }
    }

    /// <summary>
    ///  Scales an individual control's location, size, padding and margin.
    ///  If the control is top level, this will not scale the control's location.
    ///  This does not scale children or the size of auto sized controls. You can
    ///  omit scaling in any direction by changing BoundsSpecified.
    ///
    ///  After the control is scaled the RequiredScaling property is set to
    ///  BoundsSpecified.None.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void ScaleControl(SizeF factor, BoundsSpecified specified)
    {
        CreateParams cp = CreateParams;
        RECT adornments = default;
        AdjustWindowRectExForControlDpi(ref adornments, (WINDOW_STYLE)cp.Style, false, (WINDOW_EX_STYLE)cp.ExStyle);
        Size minSize = MinimumSize;
        Size maxSize = MaximumSize;

        // Clear out min and max size, otherwise this could affect the scaling logic.
        MinimumSize = Size.Empty;
        MaximumSize = Size.Empty;

        // This is raw because Min/Max size have been cleared at this point.
        Rectangle rawScaledBounds = GetScaledBounds(Bounds, factor, specified);

        //
        // Scale Padding and Margin
        //

        float dx = factor.Width;
        float dy = factor.Height;

        Padding padding = Padding;
        Padding margins = Margin;

        if (dx != 1.0F)
        {
            padding.Left = (int)Math.Round(padding.Left * dx);
            padding.Right = (int)Math.Round(padding.Right * dx);
            margins.Left = (int)Math.Round(margins.Left * dx);
            margins.Right = (int)Math.Round(margins.Right * dx);
        }

        if (dy != 1.0F)
        {
            padding.Top = (int)Math.Round(padding.Top * dy);
            padding.Bottom = (int)Math.Round(padding.Bottom * dy);
            margins.Top = (int)Math.Round(margins.Top * dy);
            margins.Bottom = (int)Math.Round(margins.Bottom * dy);
        }

        // Apply padding and margins
        Padding = padding;
        Margin = margins;

        //
        // Scale Min/Max size
        //

        // make sure we consider the adornments as fixed. rather than scaling the entire size,
        // we should pull out the fixed things such as the border, scale the rest, then apply the fixed
        // adornment size.
        Size adornmentSize = adornments.Size;
        if (!minSize.IsEmpty)
        {
            minSize -= adornmentSize;
            minSize = ScaleSize(
                LayoutUtils.UnionSizes(Size.Empty, minSize), // make sure we don't go below 0.
                factor.Width,
                factor.Height) + adornmentSize;
        }

        if (!maxSize.IsEmpty)
        {
            maxSize -= adornmentSize;
            maxSize = ScaleSize(
                LayoutUtils.UnionSizes(Size.Empty, maxSize), // make sure we don't go below 0.
                factor.Width,
                factor.Height) + adornmentSize;
        }

        // Apply the min/max size constraints - don't call ApplySizeConstraints
        // as MinimumSize/MaximumSize are currently cleared out.
        Size maximumSize = LayoutUtils.ConvertZeroToUnbounded(maxSize);
        Size scaledSize = LayoutUtils.IntersectSizes(rawScaledBounds.Size, maximumSize);
        scaledSize = LayoutUtils.UnionSizes(scaledSize, minSize);

        if (ScaleHelper.IsScalingRequirementMet
            && ParentInternal is { } parent
            && (parent.LayoutEngine == DefaultLayout.Instance))
        {
            // We need to scale AnchorInfo to update distances to container edges
            DefaultLayout.ScaleAnchorInfo(this, factor);
        }

        // Set in the scaled bounds as constrained by the newly scaled min/max size.
        SetBoundsCore(rawScaledBounds.X, rawScaledBounds.Y, scaledSize.Width, scaledSize.Height, BoundsSpecified.All);

        MaximumSize = maxSize;
        MinimumSize = minSize;
    }

    /// <summary>
    ///  Performs the work of scaling the entire control and any child controls.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    protected virtual void ScaleCore(float dx, float dy)
    {
        using SuspendLayoutScope scope = new(this);

        int sx = (int)Math.Round(_x * dx);
        int sy = (int)Math.Round(_y * dy);

        int sw = _width;
        if ((_controlStyle & ControlStyles.FixedWidth) != ControlStyles.FixedWidth)
        {
            sw = (int)(Math.Round((_x + _width) * dx)) - sx;
        }

        int sh = _height;
        if ((_controlStyle & ControlStyles.FixedHeight) != ControlStyles.FixedHeight)
        {
            sh = (int)(Math.Round((_y + _height) * dy)) - sy;
        }

        SetBounds(sx, sy, sw, sh, BoundsSpecified.All);

        if (ChildControls is { } children)
        {
            for (int i = 0; i < children.Count; i++)
            {
#pragma warning disable CS0618 // Type or member is obsolete - compat
                children[i].Scale(dx, dy);
#pragma warning restore CS0618
            }
        }
    }

    /// <summary>
    ///  Scales a given size with the provided values.
    /// </summary>
    internal Size ScaleSize(Size startSize, float x, float y)
    {
        Size size = startSize;
        if (!GetStyle(ControlStyles.FixedWidth))
        {
            size.Width = (int)Math.Round(size.Width * x);
        }

        if (!GetStyle(ControlStyles.FixedHeight))
        {
            size.Height = (int)Math.Round(size.Height * y);
        }

        return size;
    }

    /// <summary>
    ///  Activates this control.
    /// </summary>
    public void Select()
    {
        Select(false, false);
    }

    // used by Form
    protected virtual void Select(bool directed, bool forward)
    {
        IContainerControl? c = GetContainerControl();

        if (c is not null)
        {
            c.ActiveControl = this;
        }
    }

    /// <summary>
    ///  Selects the next control following ctl.
    /// </summary>
    public bool SelectNextControl(Control? ctl, bool forward, bool tabStopOnly, bool nested, bool wrap)
    {
        Control? nextSelectableControl = GetNextSelectableControl(ctl, forward, tabStopOnly, nested, wrap);
        if (nextSelectableControl is not null)
        {
            nextSelectableControl.Select(true, forward);
            return true;
        }
        else
        {
            return false;
        }
    }

    private Control? GetNextSelectableControl(Control? ctl, bool forward, bool tabStopOnly, bool nested, bool wrap)
    {
        if (!Contains(ctl) || (!nested && ctl._parent != this))
        {
            ctl = null;
        }

        bool alreadyWrapped = false;
        Control? start = ctl;
        do
        {
            ctl = GetNextControl(ctl, forward);
            if (ctl is null)
            {
                if (!wrap)
                {
                    break;
                }

                if (alreadyWrapped)
                {
                    return null; // prevent infinite wrapping.
                }

                alreadyWrapped = true;
            }
            else
            {
                if (ctl.CanSelect
                    && (!tabStopOnly || ctl.TabStop)
                    && (nested || ctl._parent == this))
                {
                    if (ctl._parent is ToolStrip)
                    {
                        continue;
                    }

                    return ctl;
                }
            }
        }
        while (ctl != start);

        return null;
    }

    /// <summary>
    ///  This is called recursively when visibility is changed for a control, this
    ///  forces focus to be moved to a visible control.
    /// </summary>
    private void SelectNextIfFocused()
    {
        // We want to move focus away from hidden controls, so this function was added.
        if (ContainsFocus && ParentInternal is not null)
        {
            IContainerControl? c = ParentInternal.GetContainerControl();

            if (c is not null)
            {
                ((Control)c).SelectNextControl(this, true, true, true, true);
            }
        }
    }

    /// <summary>
    ///  sends this control to the back of the z-order
    /// </summary>
    public void SendToBack()
    {
        if (_parent is not null)
        {
            _parent.Controls.SetChildIndex(this, -1);
        }
        else if (IsHandleCreated && GetTopLevel())
        {
            PInvoke.SetWindowPos(
                this,
                HWND.HWND_BOTTOM,
                0, 0, 0, 0,
                SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
        }
    }

    /// <summary>
    ///  Sets the bounds of the control.
    /// </summary>
    public void SetBounds(int x, int y, int width, int height)
    {
        if (_x != x || _y != y || _width != width || _height != height)
        {
            _forceAnchorCalculations = LocalAppContextSwitches.AnchorLayoutV2;
            try
            {
                SetBoundsCore(x, y, width, height, BoundsSpecified.All);
            }
            finally
            {
                _forceAnchorCalculations = false;
            }

            // WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will
            // have refreshed the interior layout. We only need to layout the parent.
            LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds);
        }
        else
        {
            // Still need to init scaling.
            InitScaling(BoundsSpecified.All);
        }
    }

    /// <summary>
    ///  Sets the bounds of the control.
    /// </summary>
    public void SetBounds(int x, int y, int width, int height, BoundsSpecified specified)
    {
        if ((specified & BoundsSpecified.X) == BoundsSpecified.None)
        {
            x = _x;
        }

        if ((specified & BoundsSpecified.Y) == BoundsSpecified.None)
        {
            y = _y;
        }

        if ((specified & BoundsSpecified.Width) == BoundsSpecified.None)
        {
            width = _width;
        }

        if ((specified & BoundsSpecified.Height) == BoundsSpecified.None)
        {
            height = _height;
        }

        if (_x != x || _y != y || _width != width || _height != height)
        {
            _forceAnchorCalculations = LocalAppContextSwitches.AnchorLayoutV2;
            try
            {
                SetBoundsCore(x, y, width, height, specified);
            }
            finally
            {
                _forceAnchorCalculations = false;
            }

            // WM_WINDOWPOSCHANGED will trickle down to an OnResize() which will
            // have refreshed the interior layout or the resized control. We only need to layout
            // the parent. This happens after InitLayout has been invoked.
            LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds);
        }
        else
        {
            // Still need to init scaling.
            InitScaling(specified);
        }
    }

    /// <summary>
    ///  Performs the work of setting the specified bounds of this control.
    /// </summary>
    /// <param name="x">The new <see cref="Left" /> property value of the control.</param>
    /// <param name="y">The new <see cref="Top" /> property value of the control.</param>
    /// <param name="width">The new <see cref="Width" /> property value of the control.</param>
    /// <param name="height">The new <see cref="Height" /> property value of the control.</param>
    /// <param name="specified">A bitwise combination of the <see cref="BoundsSpecified"/> values.</param>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        // SetWindowPos below sends a WmWindowPositionChanged (not posts) so we immediately
        // end up in WmWindowPositionChanged which may cause the parent to layout. We need to
        // suspend/resume to defer the parent from laying out until after InitLayout has been called
        // to update the layout engine's state with the new control bounds.

        if (_x == x && _y == y && _width == width && _height == height)
        {
            return;
        }

        using SuspendLayoutScope scope = new(ParentInternal);

        try
        {
            CommonProperties.UpdateSpecifiedBounds(this, x, y, width, height, specified);

            // Provide control with an opportunity to apply self imposed constraints on its size.
            Rectangle adjustedBounds = ApplyBoundsConstraints(x, y, width, height);
            width = adjustedBounds.Width;
            height = adjustedBounds.Height;
            x = adjustedBounds.X;
            y = adjustedBounds.Y;

            if (!IsHandleCreated)
            {
                // Handle is not created, just record our new position and we're done.
                UpdateBounds(x, y, width, height);
            }
            else
            {
                if (!GetState(States.SizeLockedByOS))
                {
                    SET_WINDOW_POS_FLAGS flags = SET_WINDOW_POS_FLAGS.SWP_NOZORDER | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE;

                    if (_x == x && _y == y)
                    {
                        flags |= SET_WINDOW_POS_FLAGS.SWP_NOMOVE;
                    }

                    if (_width == width && _height == height)
                    {
                        flags |= SET_WINDOW_POS_FLAGS.SWP_NOSIZE;
                    }

                    // Give a chance for derived controls to do what they want, just before we resize.
                    OnBoundsUpdate(x, y, width, height);

                    PInvoke.SetWindowPos(this, HWND.Null, x, y, width, height, flags);

                    // NOTE: SetWindowPos causes a WM_WINDOWPOSCHANGED which is processed
                    // synchronously so we effectively end up in UpdateBounds immediately following
                    // SetWindowPos.
                    //
                    // UpdateBounds(x, y, width, height);
                }
            }
        }
        finally
        {
            // Initialize the scaling engine.
            InitScaling(specified);

            if (ParentInternal is not null)
            {
                // Some layout engines (DefaultLayout) base their PreferredSize on
                // the bounds of their children. If we change change the child bounds, we
                // need to clear their PreferredSize cache. The semantics of SetBoundsCore
                // is that it does not cause a layout, so we just clear.
                CommonProperties.xClearPreferredSizeCache(ParentInternal);

                // Cause the current control to initialize its layout (e.g., Anchored controls
                // memorize their distance from their parent's edges). It is your parent's
                // LayoutEngine which manages your layout, so we call into the parent's
                // LayoutEngine.
                ParentInternal.LayoutEngine.InitLayout(this, specified);
            }
        }
    }

    /// <summary>
    ///  Performs the work of setting the size of the client area of the control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual void SetClientSizeCore(int x, int y)
    {
        Size = SizeFromClientSizeInternal(new(x, y));
        _clientWidth = x;
        _clientHeight = y;
        OnClientSizeChanged(EventArgs.Empty);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected virtual Size SizeFromClientSize(Size clientSize) => SizeFromClientSizeInternal(clientSize);

    internal Size SizeFromClientSizeInternal(Size size)
    {
        RECT rect = new(size);
        CreateParams cp = CreateParams;
        AdjustWindowRectExForControlDpi(ref rect, (WINDOW_STYLE)cp.Style, false, (WINDOW_EX_STYLE)cp.ExStyle);
        return rect.Size;
    }

    private void SetHandle(IntPtr value)
    {
        if (value == IntPtr.Zero)
        {
            SetState(States.Created, false);
        }

        UpdateRoot();
    }

    private void SetParentHandle(HWND value)
    {
        Debug.Assert(value != -1, "Outdated call to SetParentHandle");

        if (IsHandleCreated)
        {
            HWND parentHandle = PInvoke.GetParent(this);
            bool topLevel = GetTopLevel();
            if (parentHandle != value || (parentHandle.IsNull && !topLevel))
            {
                Debug.Assert(Handle != value, "Cycle created in SetParentHandle");

                bool recreate = (parentHandle.IsNull && !topLevel)
                    || (value.IsNull && topLevel);

                if (recreate)
                {
                    // We will recreate later, when the MdiChild's visibility
                    // is set to true (see
                    if (this is Form f)
                    {
                        if (!f.CanRecreateHandle())
                        {
                            recreate = false;
                            // we don't want to recreate - but our styles may have changed.
                            // before we unpark the window below we need to update
                            UpdateStyles();
                        }
                    }
                }

                if (recreate)
                {
                    RecreateHandle();
                }

                if (!GetTopLevel())
                {
                    if (value.IsNull)
                    {
                        Application.ParkHandle(handle: new(this), DpiAwarenessContext);
                        UpdateRoot();
                    }
                    else
                    {
                        if (PInvoke.SetParent(this, value).IsNull)
                        {
                            // Somehow we failed to SetParent, e.g. due to different Dpi awareness setting.
                            // Throwing exception will keep the handle parked inside ParkingWindow if recreate == true.
                            throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed);
                        }

                        _parent?.UpdateChildZOrder(this);

                        Application.UnparkHandle(this, _window.DpiAwarenessContext);
                    }
                }
            }
            else if (value.IsNull && parentHandle.IsNull && topLevel)
            {
                // The handle was previously parented to the parking window. Its TopLevel property was
                // then changed to true so the above call to GetParent returns null even though the parent of the control is
                // not null. We need to explicitly set the parent to null.
                if (PInvoke.SetParent(this, HWND.Null).IsNull)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed);
                }

                Application.UnparkHandle(this, _window.DpiAwarenessContext);
            }
        }
    }

    private protected void SetState(States flag, bool value)
    {
        _state = value ? _state | flag : _state & ~flag;
    }

    private protected void SetExtendedState(ExtendedStates flag, bool value)
    {
        _extendedState = value ? _extendedState | flag : _extendedState & ~flag;
    }

    /// <summary>
    ///  Sets the current value of the specified bit in the control's style.
    ///  NOTE: This is control style, not the Win32 style of the hWnd.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void SetStyle(ControlStyles flag, bool value)
    {
        // WARNING: if we ever add argument checking to "flag", we will need
        // to move private styles like Layered to State.
        _controlStyle = value ? _controlStyle | flag : _controlStyle & ~flag;
    }

    internal virtual void SetToolTip(ToolTip toolTip)
    {
        // Control doesn't have a specific logic after a toolTip is set
    }

    protected void SetTopLevel(bool value)
    {
        if (value && IsActiveX)
        {
            throw new InvalidOperationException(SR.TopLevelNotAllowedIfActiveX);
        }
        else
        {
            SetTopLevelInternal(value);
        }
    }

    private protected void SetTopLevelInternal(bool value)
    {
        if (GetTopLevel() != value)
        {
            if (_parent is not null)
            {
                throw new ArgumentException(SR.TopLevelParentedControl, nameof(value));
            }

            SetState(States.TopLevel, value);

            UpdateStyles();
            SetParentHandle(default);
            if (value && Visible)
            {
                CreateControl();
            }

            UpdateRoot();
        }
    }

    protected virtual void SetVisibleCore(bool value)
    {
        if (value != Visible)
        {
            if (!value)
            {
                SelectNextIfFocused();
            }

            bool fireChange = false;

#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates.
            if (GetTopLevel())
            {
                // The processing of WmShowWindow will set the visibility
                // bit and call CreateControl()
                if (IsHandleCreated || value)
                {
                    // We shouldn't mess with the color mode if users haven't specifically set it.
                    // https://github.com/dotnet/winforms/issues/12014
                    if (value && Application.ColorModeSet)
                    {
                        PrepareDarkMode(HWND, Application.IsDarkModeEnabled);
                    }

                    PInvoke.ShowWindow(HWND, value ? ShowParams : SHOW_WINDOW_CMD.SW_HIDE);
                }
            }
#pragma warning restore WFO5001
            else if (IsHandleCreated || (value && _parent?.Created == true))
            {
                // We want to mark the control as visible so that CreateControl
                // knows that we are going to be displayed... however in case
                // an exception is thrown, we need to back the change out.

                SetState(States.Visible, value);
                fireChange = true;
                try
                {
                    if (value)
                    {
                        CreateControl();
                    }

                    PInvoke.SetWindowPos(
                        this,
                        HWND.Null,
                        0, 0, 0, 0,
                        SET_WINDOW_POS_FLAGS.SWP_NOSIZE
                            | SET_WINDOW_POS_FLAGS.SWP_NOMOVE
                            | SET_WINDOW_POS_FLAGS.SWP_NOZORDER
                            | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE
                            | (value ? SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW : SET_WINDOW_POS_FLAGS.SWP_HIDEWINDOW));
                }
                catch
                {
                    SetState(States.Visible, !value);
                    throw;
                }
            }

            if (value != Visible)
            {
                SetState(States.Visible, value);
                fireChange = true;
            }

            if (fireChange)
            {
                // We do not do this in the OnPropertyChanged event for visible
                // Lots of things could cause us to become visible, including a
                // parent window. We do not want to indiscriminately layout
                // due to this, but we do want to layout if the user changed
                // our visibility.
                using (new LayoutTransaction(_parent, this, PropertyNames.Visible))
                {
                    OnVisibleChanged(EventArgs.Empty);
                }
            }

            UpdateRoot();
        }
        else
        {
            // value of Visible property not changed, but raw bit may have

            if (!DesiredVisibility && !value && IsHandleCreated)
            {
                // PERF - setting Visible=false twice can get us into this else block
                // which makes us process WM_WINDOWPOS* messages - make sure we've already
                // visible=false - if not, make it so.
                if (!PInvoke.IsWindowVisible(this))
                {
                    // we're already invisible - bail.
                    return;
                }
            }

            SetState(States.Visible, value);

            // If the handle is already created, we need to update the window style.
            // This situation occurs when the parent control is not currently visible,
            // but the child control has already been created.
            if (IsHandleCreated)
            {
                PInvoke.SetWindowPos(
                    this,
                    HWND.HWND_TOP,
                    0, 0, 0, 0,
                    SET_WINDOW_POS_FLAGS.SWP_NOSIZE
                        | SET_WINDOW_POS_FLAGS.SWP_NOMOVE
                        | SET_WINDOW_POS_FLAGS.SWP_NOZORDER
                        | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE
                        | (value ? SET_WINDOW_POS_FLAGS.SWP_SHOWWINDOW : SET_WINDOW_POS_FLAGS.SWP_HIDEWINDOW));
            }
        }

        static unsafe void PrepareDarkMode(HWND hwnd, bool darkModeEnabled)
        {
            BOOL value = darkModeEnabled;

            PInvoke.DwmSetWindowAttribute(
                hwnd,
                DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
                &value,
                (uint)sizeof(BOOL)).AssertSuccess();
        }
    }

    /// <summary>
    ///  Determine effective auto-validation setting for a given control, based on the AutoValidate property
    ///  of its containing control. Defaults to 'EnablePreventFocusChange' if there is no containing control
    ///  (eg. because this control is a top-level container).
    /// </summary>
    internal static AutoValidate GetAutoValidateForControl(Control control)
    {
        ContainerControl? parent = control.ParentContainerControl;
        return (parent is not null) ? parent.AutoValidate : AutoValidate.EnablePreventFocusChange;
    }

    /// <summary>
    ///  Is auto-validation currently in effect for this control?
    ///  Depends on the AutoValidate property of the containing control.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal bool ShouldAutoValidate
    {
        get
        {
            return GetAutoValidateForControl(this) != AutoValidate.Disable;
        }
    }

    // This method is called in PerformContainerValidation to check if this control supports containerValidation.
    // TabControl overrides this method to return true.
    internal virtual bool ShouldPerformContainerValidation()
    {
        return GetStyle(ControlStyles.ContainerControl);
    }

    /// <summary>
    ///  Returns true if the backColor should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeBackColor()
    {
        Color backColor = Properties.GetValueOrDefault<Color>(s_backColorProperty);
        return !backColor.IsEmpty;
    }

    /// <summary>
    ///  Returns true if the cursor should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeCursor() => Properties.ContainsKey(s_cursorProperty);

    /// <summary>
    ///  Returns true if the enabled property should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    private bool ShouldSerializeEnabled() => !GetState(States.Enabled);

    /// <summary>
    ///  Returns true if the foreColor should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeForeColor() => !Properties.GetValueOrDefault<Color>(s_foreColorProperty).IsEmpty;

    /// <summary>
    ///  Returns true if the font should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeFont() => Properties.ContainsKey(s_fontProperty);

    /// <summary>
    ///  Returns true if the RightToLeft should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeRightToLeft() =>
        Properties.TryGetValue(s_rightToLeftProperty, out RightToLeft rightToLeft)
            && rightToLeft != RightToLeft.Inherit;

    /// <summary>
    ///  Returns true if the visible property should be persisted in code gen.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    private bool ShouldSerializeVisible() => !DesiredVisibility;

    // Helper function - translates text alignment for Rtl controls
    // Read TextAlign as Left == Near, Right == Far
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected HorizontalAlignment RtlTranslateAlignment(HorizontalAlignment align)
    {
        return RtlTranslateHorizontal(align);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected LeftRightAlignment RtlTranslateAlignment(LeftRightAlignment align)
    {
        return RtlTranslateLeftRight(align);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected ContentAlignment RtlTranslateAlignment(ContentAlignment align)
    {
        return RtlTranslateContent(align);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected HorizontalAlignment RtlTranslateHorizontal(HorizontalAlignment align)
    {
        if (RightToLeft == RightToLeft.Yes)
        {
            if (align == HorizontalAlignment.Left)
            {
                return HorizontalAlignment.Right;
            }
            else if (align == HorizontalAlignment.Right)
            {
                return HorizontalAlignment.Left;
            }
        }

        return align;
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected LeftRightAlignment RtlTranslateLeftRight(LeftRightAlignment align)
    {
        if (RightToLeft == RightToLeft.Yes)
        {
            if (align == LeftRightAlignment.Left)
            {
                return LeftRightAlignment.Right;
            }
            else if (align == LeftRightAlignment.Right)
            {
                return LeftRightAlignment.Left;
            }
        }

        return align;
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected internal ContentAlignment RtlTranslateContent(ContentAlignment align)
    {
        if (RightToLeft == RightToLeft.Yes)
        {
            if ((align & WindowsFormsUtils.AnyTopAlign) != 0)
            {
                switch (align)
                {
                    case ContentAlignment.TopLeft:
                        return ContentAlignment.TopRight;
                    case ContentAlignment.TopRight:
                        return ContentAlignment.TopLeft;
                }
            }

            if ((align & WindowsFormsUtils.AnyMiddleAlign) != 0)
            {
                switch (align)
                {
                    case ContentAlignment.MiddleLeft:
                        return ContentAlignment.MiddleRight;
                    case ContentAlignment.MiddleRight:
                        return ContentAlignment.MiddleLeft;
                }
            }

            if ((align & WindowsFormsUtils.AnyBottomAlign) != 0)
            {
                switch (align)
                {
                    case ContentAlignment.BottomLeft:
                        return ContentAlignment.BottomRight;
                    case ContentAlignment.BottomRight:
                        return ContentAlignment.BottomLeft;
                }
            }
        }

        return align;
    }

    private void SetWindowFont() => PInvokeCore.SendMessage(this, PInvokeCore.WM_SETFONT, (WPARAM)FontHandle, (LPARAM)(BOOL)false);

    private void SetWindowStyle(int flag, bool value)
    {
        int styleFlags = (int)PInvokeCore.GetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE);
        PInvokeCore.SetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_STYLE, value ? styleFlags | flag : styleFlags & ~flag);
    }

    /// <summary>
    ///  Makes the control display by setting the visible property to true
    /// </summary>
    public void Show()
    {
        Visible = true;
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    internal bool ShouldSerializeMargin()
    {
        return !Margin.Equals(DefaultMargin);
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeMaximumSize()
    {
        return MaximumSize != DefaultMaximumSize;
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeMinimumSize()
    {
        return MinimumSize != DefaultMinimumSize;
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    internal bool ShouldSerializePadding()
    {
        return !Padding.Equals(DefaultPadding);
    }

    /// <summary>
    ///  Determines if the <see cref="Size"/> property needs to be persisted.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeSize()
    {
        // In Whidbey the ControlDesigner class will always serialize size as it replaces the Size
        // property descriptor with its own. This is here for compat.
        Size s = DefaultSize;
        return _width != s.Width || _height != s.Height;
    }

    /// <summary>
    ///  Determines if the <see cref="Text"/> property needs to be persisted.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    internal virtual bool ShouldSerializeText()
    {
        return Text.Length != 0;
    }

    /// <summary>
    ///  Suspends the layout logic for the control.
    /// </summary>
    public void SuspendLayout()
    {
        LayoutSuspendCount++;
        if (LayoutSuspendCount == 1)
        {
            OnLayoutSuspended();
        }

        Debug.Assert(LayoutSuspendCount > 0, "SuspendLayout: layoutSuspendCount overflowed.");
    }

    /// <summary>
    ///  Retrieve Font from property bag. This is the Font that was explicitly set on control by the application.
    /// </summary>
    private protected bool TryGetExplicitlySetFont([NotNullWhen(true)] out Font? font) =>
        Properties.TryGetValue(s_fontProperty, out font);

    /// <summary>
    ///  Sets the scaled font value with the option to control whether <see cref="OnFontChanged(EventArgs)"/> event is raised.
    /// </summary>
    /// <param name="scaledFont">The scaled <see cref="Font"/> value to be set.</param>
    /// <param name="raiseOnFontChangedEvent">Indicates whether to raise <see cref="OnFontChanged(EventArgs)"/> event.</param>
    private protected void SetScaledFont(Font scaledFont, bool raiseOnFontChangedEvent = true)
    {
        Debug.Assert(scaledFont is not null);

        // Store new scaled value
        Properties.AddOrRemoveValue(s_fontProperty, scaledFont);

        // Dispose old FontHandle.
        DisposeFontHandle();

        if (Properties.ContainsKey(s_fontHeightProperty))
        {
            Properties.AddValue(s_fontHeightProperty, scaledFont.Height);
        }

        if (!raiseOnFontChangedEvent)
        {
            return;
        }

        // Font is an ambient property. We need to layout our parent because Font may
        // change our size. We need to layout ourselves because our children may change
        // size by inheriting the new value.
        using (new LayoutTransaction(ParentInternal, this, PropertyNames.Font))
        {
            OnFontChanged(EventArgs.Empty);
        }
    }

    /// <summary>
    ///  Stops listening for the mouse leave event.
    /// </summary>
    private void UnhookMouseEvent()
    {
        SetState(States.TrackingMouseEvent, false);
    }

    /// <summary>
    ///  Forces the control to paint any currently invalid areas.
    /// </summary>
    public void Update()
    {
        if (IsHandleCreated)
        {
            PInvoke.UpdateWindow(this);
        }
    }

    /// <summary>
    ///  Updates the bounds of the control based on the handle the control is bound to.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected internal void UpdateBounds()
    {
        RECT rect = default;
        int clientWidth = 0;
        int clientHeight = 0;

        if (IsHandleCreated)
        {
            PInvokeCore.GetClientRect(this, out rect);
            clientWidth = rect.right;
            clientHeight = rect.bottom;
            PInvokeCore.GetWindowRect(this, out rect);
            if (!GetTopLevel())
            {
                PInvokeCore.MapWindowPoints(HWND.Null, PInvoke.GetParent(this), ref rect);
            }
        }

        UpdateBounds(
            rect.left,
            rect.top,
            rect.Width,
            rect.Height,
            clientWidth,
            clientHeight);
    }

    /// <summary>
    ///  Updates the bounds of the control based on the bounds passed in.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void UpdateBounds(int x, int y, int width, int height)
    {
        // reverse-engineer the AdjustWindowRectEx call to figure out the appropriate clientWidth and clientHeight
        RECT rect = default;
        CreateParams cp = CreateParams;

        AdjustWindowRectExForControlDpi(ref rect, (WINDOW_STYLE)cp.Style, false, (WINDOW_EX_STYLE)cp.ExStyle);
        int clientWidth = width - rect.Width;
        int clientHeight = height - rect.Height;
        UpdateBounds(x, y, width, height, clientWidth, clientHeight);
    }

    /// <summary>
    ///  Updates the bounds of the control based on the bounds passed in.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void UpdateBounds(int x, int y, int width, int height, int clientWidth, int clientHeight)
    {
        bool newLocation = _x != x || _y != y;
        bool newSize = Width != width || Height != height || _clientWidth != clientWidth || _clientHeight != clientHeight;

        _x = x;
        _y = y;
        _width = width;
        _height = height;
        _clientWidth = clientWidth;
        _clientHeight = clientHeight;

        if (newLocation)
        {
            OnLocationChanged(EventArgs.Empty);
        }

        if (newSize)
        {
            OnSizeChanged(EventArgs.Empty);
            OnClientSizeChanged(EventArgs.Empty);

            // Clear PreferredSize cache for this control
            CommonProperties.xClearPreferredSizeCache(this);
            LayoutTransaction.DoLayout(ParentInternal, this, PropertyNames.Bounds);
        }
    }

    /// <summary>
    ///  Updates the binding manager bindings when the binding property changes.
    ///  We have the code here, rather than in PropertyChanged, so we don't pull
    ///  in the data assembly if it's not used.
    /// </summary>
    private void UpdateBindings()
    {
        for (int i = 0; i < DataBindings.Count; i++)
        {
            BindingContext.UpdateBinding(BindingContext, DataBindings[i]);
        }
    }

    /// <summary>
    ///  Updates the child control's position in the control array to correctly reflect its index.
    /// </summary>
    private void UpdateChildControlIndex(Control control)
    {
        // Don't reorder the child control array for tab controls. Implemented as a special case
        // in order to keep the method private.
        //
        // Also short-circuit when the Control class is instantiated directly. This is to provide
        // consistency with the behavior prior to bug fix https://github.com/dotnet/winforms/issues/7837
        if (this is TabControl || GetType() == typeof(Control))
        {
            return;
        }

        int newIndex = 0;
        int currentIndex = Controls.GetChildIndex(control);
        HWND hWnd = control.InternalHandle;
        while (!(hWnd = PInvoke.GetWindow(hWnd, GET_WINDOW_CMD.GW_HWNDPREV)).IsNull)
        {
            Control? previousControl = FromHandle(hWnd);
            if (previousControl is not null)
            {
                newIndex = Controls.GetChildIndex(previousControl, throwException: false) + 1;
                break;
            }
        }

        if (newIndex > currentIndex)
        {
            newIndex--;
        }

        if (newIndex != currentIndex)
        {
            Controls.SetChildIndex(control, newIndex);
        }
    }

    private void UpdateReflectParent()
    {
        // WM_REFLECT messages (e.g. WM_NOTIFY, WM_DRAWITEM, etc) will always be sent to the original parent HWND. As
        // such, we need to track our parent HWND to see if it is changed so that we can recreate our own handle.
        //
        // Scenario is when you've got a control in one parent, you move it to another, then destroy the first parent. It'll stop
        // getting any reflected messages because Windows will send them to the original parent.
        //
        // See:
        //
        // https://learn.microsoft.com/cpp/mfc/tn061-on-notify-and-wm-notify-messages
        // https://learn.microsoft.com/cpp/mfc/tn062-message-reflection-for-windows-controls?view=msvc-170
        // https://learn.microsoft.com/windows/win32/controls/wm-notify
        // https://learn.microsoft.com/windows/win32/controls/wm-drawitem

        if (!Disposing && IsHandleCreated)
        {
            HWND parentHandle = PInvoke.GetParent(this);
            if (!parentHandle.IsNull)
            {
                ReflectParent = FromHandle(parentHandle);
                return;
            }
        }

        ReflectParent = null;
    }

    /// <summary>
    ///  Updates this control in it's parent's Z-order.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void UpdateZOrder()
    {
        _parent?.UpdateChildZOrder(this);
    }

    /// <summary>
    ///  Syncs the Z-order of child control to the index we want it to be.
    /// </summary>
    private void UpdateChildZOrder(Control control)
    {
        if (!IsHandleCreated || !control.IsHandleCreated || control._parent != this)
        {
            return;
        }

        HWND previous = HWND.HWND_TOP;
        for (int i = Controls.GetChildIndex(control); --i >= 0;)
        {
            Control child = Controls[i];
            if (child.IsHandleCreated && child._parent == this)
            {
                previous = child.HWND;
                break;
            }
        }

        if (PInvoke.GetWindow(control, GET_WINDOW_CMD.GW_HWNDPREV) != previous)
        {
            _state |= States.NoZOrder;
            try
            {
                PInvoke.SetWindowPos(
                    control,
                    previous,
                    0, 0, 0, 0,
                    SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE);
            }
            finally
            {
                _state &= ~States.NoZOrder;
            }
        }
    }

    /// <summary>
    ///  Updates the rootReference in the bound window.
    ///  (Used to prevent visible top-level controls from being garbage collected)
    /// </summary>
    private void UpdateRoot()
    {
        _window.LockReference(GetTopLevel() && Visible);
    }

    /// <summary>
    ///  Forces styles to be reapplied to the handle. This function will call
    ///  CreateParams to get the styles to apply.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected void UpdateStyles()
    {
        UpdateStylesCore();

        OnStyleChanged(EventArgs.Empty);
    }

    internal virtual void UpdateStylesCore()
    {
        if (!IsHandleCreated)
        {
            return;
        }

        CreateParams cp = CreateParams;
        WINDOW_STYLE currentStyle = WindowStyle;
        WINDOW_EX_STYLE currentExtendedStyle = ExtendedWindowStyle;

        // Resolve the Form's lazy visibility.
        if ((_state & States.Visible) != 0)
        {
            cp.Style |= (int)WINDOW_STYLE.WS_VISIBLE;
        }

        if (currentStyle != (WINDOW_STYLE)cp.Style)
        {
            WindowStyle = (WINDOW_STYLE)cp.Style;
        }

        if (currentExtendedStyle != (WINDOW_EX_STYLE)cp.ExStyle)
        {
            ExtendedWindowStyle = (WINDOW_EX_STYLE)cp.ExStyle;
            SetState(States.Mirrored, ((WINDOW_EX_STYLE)cp.ExStyle).HasFlag(WINDOW_EX_STYLE.WS_EX_LAYOUTRTL));
        }

        PInvoke.SetWindowPos(
            this,
            HWND.HWND_TOP,
            0, 0, 0, 0,
            SET_WINDOW_POS_FLAGS.SWP_DRAWFRAME
                | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE
                | SET_WINDOW_POS_FLAGS.SWP_NOMOVE
                | SET_WINDOW_POS_FLAGS.SWP_NOSIZE
                | SET_WINDOW_POS_FLAGS.SWP_NOZORDER);

        Invalidate(true);
    }

    // Give a chance for derived controls to do what they want, just before we resize.
    internal virtual void OnBoundsUpdate(int x, int y, int width, int height)
    {
    }

    // These Window* methods allow us to keep access to the "window"
    // property private, which is important for restricting access to the
    // handle.
    internal void WindowAssignHandle(HWND handle, bool value)
    {
        _window.AssignHandle(handle, value);
    }

    internal void WindowReleaseHandle()
    {
        _window.ReleaseHandle();
    }

    private void WmClose(ref Message m)
    {
        if (ParentInternal is not null)
        {
            HWND parentHandle = HWND;
            HWND lastParentHandle = parentHandle;

            while (!parentHandle.IsNull)
            {
                lastParentHandle = parentHandle;
                parentHandle = PInvoke.GetParent(parentHandle);

                if (((WINDOW_STYLE)PInvokeCore.GetWindowLong(lastParentHandle, WINDOW_LONG_PTR_INDEX.GWL_STYLE))
                    .HasFlag(WINDOW_STYLE.WS_CHILD))
                {
                    break;
                }
            }

            if (!lastParentHandle.IsNull)
            {
                PInvokeCore.PostMessage(lastParentHandle, PInvokeCore.WM_CLOSE);
            }
        }

        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_CAPTURECHANGED message
    /// </summary>
    private void WmCaptureChanged(ref Message m)
    {
        OnMouseCaptureChanged(EventArgs.Empty);
        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_COMMAND message
    /// </summary>
    private void WmCommand(ref Message m)
    {
        if (m.LParamInternal == 0)
        {
            if (Command.DispatchID(m.WParamInternal.LOWORD))
            {
                return;
            }
        }
        else
        {
            if (ReflectMessage(m.LParamInternal, ref m))
            {
                return;
            }
        }

        DefWndProc(ref m);
    }

    // Overridable so nested controls can provide a different source control.
    internal virtual void WmContextMenu(ref Message m)
    {
        WmContextMenu(ref m, this);
    }

    /// <summary>
    ///  Handles the WM_CONTEXTMENU message
    /// </summary>
    internal void WmContextMenu(ref Message m, Control sourceControl)
    {
        if (!Properties.TryGetValue(s_contextMenuStripProperty, out ContextMenuStrip? contextMenuStrip))
        {
            DefWndProc(ref m);
            return;
        }

        int x = PARAM.SignedLOWORD(m.LParamInternal);
        int y = PARAM.SignedHIWORD(m.LParamInternal);
        Point client;
        bool keyboardActivated = false;

        // lparam will be -1 when the user invokes the context menu with the keyboard.
        if (m.LParamInternal == -1)
        {
            keyboardActivated = true;
            client = new Point(Width / 2, Height / 2);
        }
        else
        {
            client = PointToClient(new Point(x, y));
        }

        if (ClientRectangle.Contains(client))
        {
            contextMenuStrip.ShowInternal(sourceControl, client, keyboardActivated);
        }
        else
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_CTLCOLOR message
    /// </summary>
    private void WmCtlColorControl(ref Message m)
    {
        // We could simply reflect the message, but it's faster to handle it here if possible.
        Control? control = FromHandle(m.LParamInternal);
        if (control is not null)
        {
            m.ResultInternal = (LRESULT)(nint)control.InitializeDCForWmCtlColor((HDC)(nint)m.WParamInternal, m.MsgInternal);
            if (m.ResultInternal != 0)
            {
                return;
            }
        }

        DefWndProc(ref m);
    }

    private void WmDisplayChange(ref Message m)
    {
        BufferedGraphicsManager.Current.Invalidate();

        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_ERASEBKGND message
    /// </summary>
    private void WmEraseBkgnd(ref Message m)
    {
        if (GetStyle(ControlStyles.UserPaint))
        {
            // When possible, it's best to do all painting directly from WM_PAINT.
            // OptimizedDoubleBuffer is the "same" as turning on AllPaintingInWMPaint
            if (!GetStyle(ControlStyles.AllPaintingInWmPaint))
            {
                HDC dc = (HDC)(nint)m.WParamInternal;
                if (dc.IsNull)
                {
                    // This happens under extreme stress conditions
                    m.ResultInternal = (LRESULT)0;
                    return;
                }

                PInvokeCore.GetClientRect(this, out RECT rc);
                using PaintEventArgs pevent = new(dc, rc);
                PaintWithErrorHandling(pevent, PaintLayerBackground);
            }

            m.ResultInternal = (LRESULT)1;
        }
        else
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_GETCONTROLNAME message. Returns the name of the control.
    /// </summary>
    private void WmGetControlName(ref Message m)
    {
        MarshalStringToMessage(Site?.Name ?? Name ?? string.Empty, ref m);
    }

    /// <summary>
    ///  Handles the WM_GETCONTROLTYPE message. Returns the name of the control.
    /// </summary>
    private void WmGetControlType(ref Message m)
    {
        string type = GetType().AssemblyQualifiedName!;
        MarshalStringToMessage(type, ref m);
    }

    /// <summary>
    ///  Handles the WM_GETOBJECT message. Used for accessibility.
    /// </summary>
    private unsafe void WmGetObject(ref Message m)
    {
        if (m.LParamInternal == PInvoke.UiaRootObjectId && SupportsUiaProviders)
        {
            // If the requested object identifier is UiaRootObjectId,
            // we should return an UI Automation provider using the UiaReturnRawElementProvider function.
            m.ResultInternal = PInvoke.UiaReturnRawElementProvider(
                this,
                m.WParamInternal,
                m.LParamInternal,
                AccessibilityObject);

            return;
        }

        int objectId = (int)m.LParamInternal;

        AccessibleObject? accessibleObject = (OBJECT_IDENTIFIER)objectId switch
        {
            OBJECT_IDENTIFIER.OBJID_CLIENT => AccessibilityObject,
            OBJECT_IDENTIFIER.OBJID_WINDOW => NcAccessibilityObject,
            _ => objectId > 0 ? GetAccessibilityObjectById(objectId) : null,
        };

        // See "How to Handle WM_GETOBJECT" in MSDN.
        if (accessibleObject is null)
        {
            // Some accessible object requested that we don't care about, so do default message processing.
            DefWndProc(ref m);

            return;
        }

        try
        {
            // Obtain the Lresult.
            m.ResultInternal = accessibleObject.GetLRESULT(m.WParamInternal);
        }
        catch (Exception e)
        {
            throw new InvalidOperationException(SR.RichControlLresult, e);
        }
    }

    /// <summary>
    ///  Handles the WM_HELP message
    /// </summary>
    private unsafe void WmHelp(ref Message m)
    {
        // If there's currently a message box open - grab the help info from it.
        HelpInfo? hpi = MessageBox.HelpInfo;
        if (hpi is not null)
        {
            switch (hpi.Option)
            {
                case HelpInfo.HelpFileOption:
                    Help.ShowHelp(this, hpi.HelpFilePath);
                    break;
                case HelpInfo.HelpKeywordOption:
                    Help.ShowHelp(this, hpi.HelpFilePath, hpi.Keyword);
                    break;
                case HelpInfo.HelpNavigatorOption:
                    Help.ShowHelp(this, hpi.HelpFilePath, hpi.Navigator);
                    break;
                case HelpInfo.HelpObjectOption:
                    Help.ShowHelp(this, hpi.HelpFilePath, hpi.Navigator, hpi.Param);
                    break;
            }
        }

        // Note: info.hItemHandle is the handle of the window that sent the help message.
        HELPINFO* info = (HELPINFO*)(nint)m.LParamInternal;
        HelpEventArgs hevent = new(info->MousePos);
        OnHelpRequested(hevent);
        if (!hevent.Handled)
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_CREATE message
    /// </summary>
    private void WmCreate(ref Message m)
    {
        DefWndProc(ref m);

        _parent?.UpdateChildZOrder(this);

        UpdateBounds();

        // Let any interested sites know that we've now created a handle
        OnHandleCreated(EventArgs.Empty);

        // this code is important -- it is critical that we stash away
        // the value of the text for controls such as edit, button,
        // label, etc. Without this processing, any time you change a
        // property that forces handle recreation, you lose your text!
        // See the below code in wmDestroy
        if (!GetStyle(ControlStyles.CacheText))
        {
            _text = null;
        }
    }

    /// <summary>
    ///  Handles the WM_DESTROY message
    /// </summary>
    private void WmDestroy(ref Message m)
    {
        // Let any interested sites know that we're destroying our handle

        if (!RecreatingHandle && !Disposing && !IsDisposed && GetState(States.TrackingMouseEvent))
        {
            // Raise the MouseLeave event for the control below the mouse
            // when a modal dialog is discarded.
            OnMouseLeave(EventArgs.Empty);
            UnhookMouseEvent();
        }

        if (SupportsUiaProviders)
        {
            ReleaseUiaProvider(HWNDInternal);
        }

        OnHandleDestroyed(EventArgs.Empty);

        if (!Disposing)
        {
            // If we are not recreating the handle, set our created state
            // back to false so we can be rebuilt if we need to be.
            if (!RecreatingHandle)
            {
                SetState(States.Created, false);
            }
        }
        else
        {
            SetState(States.Visible, false);
        }

        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_CHAR, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, and
    ///  WM_SYSKEYUP messages.
    /// </summary>
    private void WmKeyChar(ref Message m)
    {
        if (ProcessKeyMessage(ref m))
        {
            return;
        }

        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_KILLFOCUS message
    /// </summary>
    private void WmKillFocus(ref Message m)
    {
        WmImeKillFocus();
        DefWndProc(ref m);
        InvokeLostFocus(this, EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the WM_MOUSEDOWN message
    /// </summary>
    private void WmMouseDown(ref Message m, MouseButtons button, int clicks)
    {
        // If this is a "real" mouse event (not just WM_LBUTTONDOWN, etc) then we need to see if something happens
        // during processing of user code that changed the state of the buttons (i.e. bringing up a dialog) to keep
        // the control in a consistent state.
        MouseButtons realState = MouseButtons;
        SetState(States.MousePressed, true);

        // If the UserMouse style is set, the control does its own processing of mouse messages.
        if (!GetStyle(ControlStyles.UserMouse))
        {
            DefWndProc(ref m);

            // We might have re-entered the message loop and processed a WM_CLOSE message.
            if (IsDisposed)
            {
                return;
            }
        }
        else
        {
            // DefWndProc would normally set the focus to this control, but
            // since we're skipping DefWndProc, we need to do it ourselves.
            if (button == MouseButtons.Left && GetStyle(ControlStyles.Selectable))
            {
                Focus();
            }
        }

        if (realState != MouseButtons)
        {
            return;
        }

        if (!GetExtendedState(ExtendedStates.MaintainsOwnCaptureMode))
        {
            // Capture is set usually in MouseDown (ToolStrip main exception)
            Capture = true;
        }

        if (realState != MouseButtons)
        {
            return;
        }

        // Control should be enabled when this method is entered, but may have become
        // disabled during its lifetime (e.g. through a Click or Focus listener).
        if (Enabled)
        {
            OnMouseDown(new MouseEventArgs(button, clicks, PARAM.ToPoint(m.LParamInternal)));
        }
    }

    /// <summary>
    ///  Handles the WM_MOUSEENTER message
    /// </summary>
    private void WmMouseEnter(ref Message m)
    {
        DefWndProc(ref m);
        KeyboardToolTipStateMachine.Instance.NotifyAboutMouseEnter(this);
        OnMouseEnter(EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the WM_MOUSELEAVE message
    /// </summary>
    private void WmMouseLeave(ref Message m)
    {
        DefWndProc(ref m);
        OnMouseLeave(EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the WM_DPICHANGED_BEFOREPARENT message. This message is not sent to top level windows.
    /// </summary>
    private void WmDpiChangedBeforeParent(ref Message m)
    {
        DefWndProc(ref m);

        _oldDeviceDpi = _deviceDpi;

        // In order to support tests, will be querying Dpi from the message first.
        int newDeviceDpi = (short)m.WParamInternal.LOWORD;

        // On certain OS versions, for non-test scenarios, WParam may be empty.
        if (newDeviceDpi == 0)
        {
            newDeviceDpi = (int)PInvoke.GetDpiForWindow(this);
        }

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

        Font localFont = GetCurrentFontAndDpi(out int fontDpi);
        _deviceDpi = newDeviceDpi;

        if (fontDpi == _deviceDpi)
        {
            OnDpiChangedBeforeParent(EventArgs.Empty);
            return;
        }

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

        ScaledControlFont = GetScaledFont(localFont, _deviceDpi, fontDpi);

        if (isLocalFontSet || container is null || !IsScaledByParent(this))
        {
            if (isLocalFontSet)
            {
                // Container controls with the font set explicitly have their fonts scaled according to the current dpi (i.e. set to ScaledFont).
                // For container control with  AutoscaleMode=Inherit we'd like to avoid raising OnFontChanged event that would trigger
                // PerformAutoscale with the parent's AutoscaleFactor. This is because the parent control is yet to receive DpiChanged event
                // and, thus, neither scaled nor updated its AutoscaleFactor value.
                // Mark the containers as required scaling, so they are scaled when their parents update AutoscaleFactor value and ready to scale.
                bool raiseOnFontChangedEvent = container is null || container.AutoScaleMode != AutoScaleMode.Inherit;
                SetScaledFont(ScaledControlFont, raiseOnFontChangedEvent);
            }

            // Mark the container as needing to rescale when its parent is scaled.
            // This flag is reset when scaling is done on Container in "OnParentFontChanged".
            if (container is not null)
            {
                container.IsDpiChangeScalingRequired = true;
            }

            RescaleConstantsForDpi(_oldDeviceDpi, _deviceDpi);
        }

        OnDpiChangedBeforeParent(EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the WM_DPICHANGED_AFTERPARENT message
    /// </summary>
    private void WmDpiChangedAfterParent(ref Message m)
    {
        DefWndProc(ref m);

        OnDpiChangedAfterParent(EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the "WM_MOUSEHOVER" message... until we get actual OS support
    ///  for this, it is implemented as a custom message.
    /// </summary>
    private void WmMouseHover(ref Message m)
    {
        DefWndProc(ref m);
        OnMouseHover(EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the WM_MOUSEMOVE message.
    /// </summary>
    private void WmMouseMove(ref Message m)
    {
        // If the UserMouse style is set, the control does its own processing of mouse messages.
        if (!GetStyle(ControlStyles.UserMouse))
        {
            DefWndProc(ref m);
        }

        OnMouseMove(new MouseEventArgs(MouseButtons, 0, PARAM.ToPoint(m.LParamInternal)));
    }

    /// <summary>
    ///  Handles the WM_MOUSEUP message.
    /// </summary>
    private void WmMouseUp(ref Message m, MouseButtons button, int clicks)
    {
        try
        {
            Point location = PARAM.ToPoint(m.LParamInternal);
            Point screenLocation = PointToScreen(location);

            // If the UserMouse style is set, the control does its own processing of mouse messages.
            if (!GetStyle(ControlStyles.UserMouse))
            {
                DefWndProc(ref m);
            }
            else
            {
                // DefWndProc would normally trigger a context menu here (for a right button click), but since
                // we're skipping DefWndProc we have to do it ourselves.
                if (button == MouseButtons.Right)
                {
                    PInvokeCore.SendMessage(this, PInvokeCore.WM_CONTEXTMENU, (WPARAM)HWND, (LPARAM)screenLocation);
                }
            }

            bool fireClick = _controlStyle.HasFlag(ControlStyles.StandardClick)
                && GetState(States.MousePressed)
                && !IsDisposed
                && PInvoke.WindowFromPoint(screenLocation) == HWND;

            if (fireClick && !ValidationCancelled)
            {
                if (!GetState(States.DoubleClickFired))
                {
                    OnClick(new MouseEventArgs(button, clicks, location));
                    OnMouseClick(new MouseEventArgs(button, clicks, location));
                }
                else
                {
                    OnDoubleClick(new MouseEventArgs(button, 2, location));
                    OnMouseDoubleClick(new MouseEventArgs(button, 2, location));
                }
            }

            OnMouseUp(new MouseEventArgs(button, clicks, location));
        }
        finally
        {
            // Always reset the States.DoubleClickFired in UP. Since we get UP - DOWN - DBLCLK - UP sequence
            // The flag is set in L_BUTTONDBLCLK in the controls WndProc().
            SetState(States.DoubleClickFired, false);
            SetState(States.MousePressed, false);
            SetState(States.ValidationCancelled, false);

            // Capture is reset while exiting MouseUp.
            Capture = false;
        }
    }

    /// <summary>
    ///  Handles the WM_MOUSEWHEEL message.
    /// </summary>
    private void WmMouseWheel(ref Message m)
    {
        HandledMouseEventArgs e = new(
            MouseButtons.None,
            0,
            PointToClient(PARAM.ToPoint(m.LParamInternal)),
            (short)m.WParamInternal.HIWORD);

        OnMouseWheel(e);
        m.ResultInternal = (LRESULT)(BOOL)e.Handled;
        if (!e.Handled)
        {
            // Forwarding the message to the parent window.
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_MOVE message. We must do this in addition to WM_WINDOWPOSCHANGED because windows may
    ///  send WM_MOVE directly.
    /// </summary>
    private void WmMove(ref Message m)
    {
        DefWndProc(ref m);
        UpdateBounds();
    }

    /// <summary>
    ///  Handles the WM_NOTIFY message.
    /// </summary>
    private unsafe void WmNotify(ref Message m)
    {
        NMHDR* nmhdr = (NMHDR*)(nint)m.LParamInternal;
        if (!ReflectMessage(nmhdr->hwndFrom, ref m))
        {
            switch (nmhdr->code)
            {
                case PInvoke.TTN_SHOW:
                    m.ResultInternal = PInvokeCore.SendMessage(
                        nmhdr->hwndFrom,
                        MessageId.WM_REFLECT | m.MsgInternal,
                        m.WParamInternal,
                        m.LParamInternal);
                    return;
                case PInvoke.TTN_POP:
                    PInvokeCore.SendMessage(
                        nmhdr->hwndFrom,
                        MessageId.WM_REFLECT | m.MsgInternal,
                        m.WParamInternal,
                        m.LParamInternal);
                    break;
            }

            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_NOTIFYFORMAT message.
    /// </summary>
    private void WmNotifyFormat(ref Message m)
    {
        if (!ReflectMessage((nint)m.WParamInternal, ref m))
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_DRAWITEM\WM_MEASUREITEM messages for controls other than menus.
    /// </summary>
    private void WmOwnerDraw(ref Message m)
    {
        int controlId = (int)m.WParamInternal;
        HWND dialogItem = PInvoke.GetDlgItem(m.HWND, controlId);
        if (dialogItem.IsNull)
        {
            // On 64-bit platforms wParam is already 64 bit but the control ID stored in it is only 32-bit.
            // Empirically, we have observed that the 64 bit HWND is just a sign extension of the 32-bit ctrl ID
            // Since WParam is already 64-bit, we need to discard the high dword first and then re-extend the
            // 32-bit value treating it as signed.
            dialogItem = (HWND)controlId;
        }

        if (ReflectMessage(dialogItem, ref m))
        {
            return;
        }

        // Try the parameter as a WindowId. TabControl truncates the HWND value.
        HWND handle = NativeWindow.GetHandleFromWindowId((short)m.WParamInternal.LOWORD);
        if (!handle.IsNull && FromHandle(handle) is { } control)
        {
            m.ResultInternal = PInvokeCore.SendMessage(
                control,
                MessageId.WM_REFLECT | m.MsgInternal,
                (WPARAM)handle, m.LParamInternal);

            return;
        }

        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_PAINT messages. This should only be called for userpaint controls.
    /// </summary>
    private void WmPaint(ref Message m)
    {
        bool doubleBuffered = DoubleBuffered || (GetStyle(ControlStyles.AllPaintingInWmPaint) && DoubleBufferingEnabled);
#if DEBUG
        if (s_bufferDisabled.Enabled)
        {
            doubleBuffered = false;
        }
#endif
        Rectangle clip;
        HDC dc = (HDC)(nint)m.WParamInternal;

        bool usingBeginPaint = dc.IsNull;
        using var paintScope = usingBeginPaint ? new BeginPaintScope(HWND) : default;

        if (usingBeginPaint)
        {
            dc = paintScope!.HDC;
            clip = paintScope.PaintRectangle;
        }
        else
        {
            clip = ClientRectangle;
        }

        // Consider: Why don't check the clip condition when non-doubleBuffered?
        //           we should probably get rid of the !doubleBuffered condition.
        if (doubleBuffered && (clip.Width <= 0 || clip.Height <= 0))
        {
            return;
        }

        BufferedGraphics? bufferedGraphics = null;
        PaintEventArgs? pevent = null;

        using var paletteScope = doubleBuffered || usingBeginPaint
            ? dc.HalftonePalette(forceBackground: false, realizePalette: false)
            : default;

        bool paintBackground = (usingBeginPaint && GetStyle(ControlStyles.AllPaintingInWmPaint)) || doubleBuffered;

        if (doubleBuffered)
        {
            try
            {
                bufferedGraphics = BufferContext.Allocate(dc, ClientRectangle);

#if DEBUG
                if (s_bufferPinkRect.Enabled)
                {
                    Rectangle band = ClientRectangle;
                    using (BufferedGraphics bufferedGraphics2 = BufferContext.Allocate(dc, band))
                    {
                        bufferedGraphics2.Graphics.FillRectangle(Brushes.Red, band);
                        bufferedGraphics2.Render();
                    }

                    Thread.Sleep(50);
                }
#endif
            }
            catch (Exception ex) when (!ex.IsCriticalException() || ex is OutOfMemoryException)
            {
                // BufferContext.Allocate will throw out of memory exceptions when it fails to create a device
                // dependent bitmap while trying to get information about the device we are painting on.
                //
                // That is not the same as a system running out of memory and there is a very good chance that we
                // can continue to paint successfully. We cannot check whether double buffering is supported in
                // this case, and we will disable it.
                //
                // We could set a specific string when throwing the exception and check for it here to distinguish
                // between that case and real out of memory exceptions but we see no reasons justifying the
                // additional complexity.

#if DEBUG
                if (s_bufferPinkRect.Enabled)
                {
                    Debug.WriteLine("Could not create buffered graphics, will paint in the surface directly");
                }
#endif
                doubleBuffered = false; // paint directly on the window DC.
            }
        }

        if (bufferedGraphics is not null)
        {
            bufferedGraphics.Graphics.SetClip(clip);
            pevent = new PaintEventArgs(
                bufferedGraphics.Graphics,
                clip,
                // We've applied a Clip, so we need to apply it when we draw
                (paintBackground ? DrawingEventFlags.SaveState : default) | DrawingEventFlags.GraphicsStateUnclean);
        }
        else
        {
            pevent = new PaintEventArgs(
                dc,
                clip,
                paintBackground ? DrawingEventFlags.SaveState : default);
        }

        using (pevent)
        {
            if (paintBackground)
            {
                PaintWithErrorHandling(pevent, PaintLayerBackground);
                pevent.ResetGraphics();
            }

            PaintWithErrorHandling(pevent, PaintLayerForeground);

            bufferedGraphics?.Render();
        }

        bufferedGraphics?.Dispose();
    }

    /// <summary>
    ///  Handles the WM_PRINTCLIENT messages.
    /// </summary>
    private void WmPrintClient(ref Message m)
    {
        HDC hdc = (HDC)(nint)m.WParamInternal;
        if (hdc.IsNull)
        {
            return;
        }

        using PaintEventArgs e = new PrintPaintEventArgs(m, hdc, ClientRectangle);
        OnPrint(e);
    }

    private void WmQueryNewPalette(ref Message m)
    {
        using GetDcScope dc = new(HWND);

        // We don't want to unset the palette in this case so we don't do this in a using.
        var paletteScope = dc.HalftonePalette(
            forceBackground: true,
            realizePalette: true);

        Invalidate(true);
        m.ResultInternal = (LRESULT)1;
        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_SETCURSOR message
    /// </summary>
    private void WmSetCursor(ref Message m)
    {
        // Accessing through the Handle property has side effects that break this logic. You must use InternalHandle.
        if ((HWND)m.WParamInternal == InternalHandle && m.LParamInternal.LOWORD == PInvoke.HTCLIENT)
        {
            Cursor.Current = Cursor;
        }
        else
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_WINDOWPOSCHANGING message.
    /// </summary>
    private unsafe void WmWindowPosChanging(ref Message m)
    {
        // We let this fall through to defwndproc unless we are being surfaced as
        // an ActiveX control. In that case, we must let the ActiveX side of things
        // manipulate our bounds here.
        if (IsActiveX)
        {
            WINDOWPOS* wp = (WINDOWPOS*)(nint)m.LParamInternal;

            // Only call UpdateBounds if the new bounds are different.
            bool different = false;

            if ((wp->flags & SET_WINDOW_POS_FLAGS.SWP_NOMOVE) == 0 && (wp->x != Left || wp->y != Top))
            {
                different = true;
            }

            if ((wp->flags & SET_WINDOW_POS_FLAGS.SWP_NOSIZE) == 0 && (wp->cx != Width || wp->cy != Height))
            {
                different = true;
            }

            if (different)
            {
                ActiveXUpdateBounds(ref wp->x, ref wp->y, ref wp->cx, ref wp->cy, wp->flags);
            }
        }

        DefWndProc(ref m);
    }

    /// <summary>
    ///  Handles the WM_PARENTNOTIFY message.
    /// </summary>
    private void WmParentNotify(ref Message m)
    {
        MessageId msg = (MessageId)(uint)m.WParamInternal.LOWORD;
        HWND hWnd = HWND.Null;
        switch (msg)
        {
            case PInvokeCore.WM_CREATE:
                hWnd = (HWND)m.LParamInternal;
                break;
            case PInvokeCore.WM_DESTROY:
                break;
            default:
                hWnd = PInvoke.GetDlgItem(this, m.WParamInternal.HIWORD);
                break;
        }

        if (hWnd.IsNull || !ReflectMessage(hWnd, ref m))
        {
            DefWndProc(ref m);
        }
    }

    /// <summary>
    ///  Handles the WM_SETFOCUS message.
    /// </summary>
    private void WmSetFocus(ref Message m)
    {
        WmImeSetFocus();

        if (!HostedInWin32DialogManager)
        {
            IContainerControl? c = GetContainerControl();
            if (c is not null)
            {
                bool activateSucceed;

                if (c is ContainerControl knowncontainer)
                {
                    activateSucceed = knowncontainer.ActivateControl(this);
                }
                else
                {
                    // Taking focus and activating a control in response to a user gesture (WM_SETFOCUS) is OK.
                    activateSucceed = c.ActivateControl(this);
                }

                if (!activateSucceed)
                {
                    return;
                }
            }
        }

        DefWndProc(ref m);
        InvokeGotFocus(this, EventArgs.Empty);
    }

    /// <summary>
    ///  Handles the WM_SHOWWINDOW message.
    /// </summary>
    private void WmShowWindow(ref Message m)
    {
        // We get this message for each control, even if their parent is not visible.

        DefWndProc(ref m);

        if (_state.HasFlag(States.Recreate))
        {
            return;
        }

        bool visible = m.WParamInternal != 0u;
        bool oldVisibleProperty = Visible;

        if (visible)
        {
            bool oldVisibleBit = GetState(States.Visible);
            SetState(States.Visible, true);
            bool executedOk = false;
            try
            {
                CreateControl();
                executedOk = true;
            }

            finally
            {
                if (!executedOk)
                {
                    // We do it this way instead of a try/catch because catching and rethrowing
                    // an exception loses call stack information
                    SetState(States.Visible, oldVisibleBit);
                }
            }
        }
        else
        {
            // Not visible. If Windows tells us it's visible, that's pretty unambiguous. But if it tells us it's
            // not visible, there's more than one explanation -- maybe the container control became invisible. So
            // we look at the parent and take a guess at the reason.

            // We do not want to update state if we are on the parking window.
            bool parentVisible = GetTopLevel();
            if (ParentInternal is not null)
            {
                parentVisible = ParentInternal.Visible;
            }

            if (parentVisible)
            {
                SetState(States.Visible, false);
            }
        }

        if (!GetState(States.ParentRecreating) && (oldVisibleProperty != visible))
        {
            OnVisibleChanged(EventArgs.Empty);
        }
    }

    /// <summary>
    ///  Handles the WM_UPDATEUISTATE message
    /// </summary>
    private void WmUpdateUIState(ref Message m)
    {
        // See "How this all works" in ShowKeyboardCues

        bool keyboard = false;
        bool focus = false;

        // check the cached values in uiCuesState to see if we've ever set in the UI state
        bool keyboardInitialized = (_uiCuesState & UICuesStates.KeyboardMask) != 0;
        bool focusInitialized = (_uiCuesState & UICuesStates.FocusMask) != 0;

        if (keyboardInitialized)
        {
            keyboard = ShowKeyboardCues;
        }

        if (focusInitialized)
        {
            focus = ShowFocusCues;
        }

        DefWndProc(ref m);

        uint cmd = m.WParamInternal.LOWORD;

        // if we're initializing, don't bother updating the uiCuesState/Firing the event.

        if (cmd == PInvoke.UIS_INITIALIZE)
        {
            return;
        }

        // Set in the cached value for uiCuesStates...

        // Windows stores the opposite of what you would think, it has bit
        // flags for the "Hidden" state, the presence of this flag means its
        // hidden, the absence thereof means it's shown.
        //
        // When we're called here with a UIS_CLEAR and the hidden state is set
        // that means we want to show the accelerator.
        UICues UIcues = UICues.None;
        if ((m.WParamInternal.HIWORD & PInvoke.UISF_HIDEACCEL) != 0)
        {
            // yes, clear means show. nice api, guys.
            bool showKeyboard = (cmd == PInvoke.UIS_CLEAR);

            if (showKeyboard != keyboard || !keyboardInitialized)
            {
                UIcues |= UICues.ChangeKeyboard;

                // Clear the old state.
                _uiCuesState &= ~UICuesStates.KeyboardMask;
                _uiCuesState |= (showKeyboard ? UICuesStates.KeyboardShow : UICuesStates.KeyboardHidden);
            }

            if (showKeyboard)
            {
                UIcues |= UICues.ShowKeyboard;
            }
        }

        // Same deal for the Focus cues as the keyboard cues.
        if ((m.WParamInternal.HIWORD & PInvoke.UISF_HIDEFOCUS) != 0)
        {
            // Yes, clear means show.
            bool showFocus = cmd == PInvoke.UIS_CLEAR;

            if (showFocus != focus || !focusInitialized)
            {
                UIcues |= UICues.ChangeFocus;

                // Clear the old state.
                _uiCuesState &= ~UICuesStates.FocusMask;
                _uiCuesState |= (showFocus ? UICuesStates.FocusShow : UICuesStates.FocusHidden);
            }

            if (showFocus)
            {
                UIcues |= UICues.ShowFocus;
            }
        }

        // Fire the UI cues state changed event.
        if ((UIcues & UICues.Changed) != 0)
        {
            OnChangeUICues(new UICuesEventArgs(UIcues));
            Invalidate(true);
        }
    }

    /// <summary>
    ///  Handles the WM_WINDOWPOSCHANGED message.
    /// </summary>
    private unsafe void WmWindowPosChanged(ref Message m)
    {
        DefWndProc(ref m);

        // Update new size / position
        UpdateBounds();
        if (IsHandleCreated
            && _parent is not null
            && PInvoke.GetParent(this) == _parent.InternalHandle
            && (_state & States.NoZOrder) == 0)
        {
            WINDOWPOS* wp = (WINDOWPOS*)(nint)m.LParamInternal;
            if ((wp->flags & SET_WINDOW_POS_FLAGS.SWP_NOZORDER) == 0)
            {
                _parent.UpdateChildControlIndex(this);
            }
        }
    }

    /// <summary>
    ///  Processes Windows messages.
    /// </summary>
    /// <remarks>
    ///  <para>
    ///   All messages are sent to the <see cref="WndProc(ref Message)"/> method after getting filtered through the
    ///   <see cref="PreProcessMessage(ref Message)"/> method.
    ///  </para>
    ///  <para>
    ///   The <see cref="WndProc(ref Message)"/> method corresponds exactly to the Windows <c>WindowProc</c>
    ///   function. For more information about processing Windows messages see the
    ///   <see href="https://go.microsoft.com/fwlink/?LinkId=181565">
    ///    WindowProc function.
    ///   </see>
    ///  </para>
    /// </remarks>
    /// <notesToInheritors>
    ///  Inheriting controls should call the base class's <see cref="WndProc(ref Message)"/> method to process any
    ///  messages that they do not handle.
    /// </notesToInheritors>
    protected virtual void WndProc(ref Message m)
    {
        // Inlined code from GetStyle(...) to ensure no perf hit for a method call.

        if ((_controlStyle & ControlStyles.EnableNotifyMessage) == ControlStyles.EnableNotifyMessage)
        {
            // Pass message *by value* to avoid the possibility of the OnNotifyMessage modifying the message.
            OnNotifyMessage(m);
        }

        // If you add any new messages below (or change the message handling code for any messages)
        // please make sure that you also modify AxHost.WndProc to do the right thing and intercept
        // messages which the Ocx would own before passing them onto Control.WndProc.
        switch (m.MsgInternal)
        {
            case PInvokeCore.WM_CAPTURECHANGED:
                WmCaptureChanged(ref m);
                break;

            case PInvokeCore.WM_GETOBJECT:
                WmGetObject(ref m);
                break;

            case PInvokeCore.WM_COMMAND:
                WmCommand(ref m);
                break;

            case PInvokeCore.WM_CLOSE:
                WmClose(ref m);
                break;

            case PInvokeCore.WM_CONTEXTMENU:
                WmContextMenu(ref m);
                break;

            case PInvokeCore.WM_DISPLAYCHANGE:
                WmDisplayChange(ref m);
                break;

            case PInvokeCore.WM_DRAWITEM:
                if (m.WParamInternal != 0u)
                {
                    WmOwnerDraw(ref m);
                }

                break;

            case PInvokeCore.WM_ERASEBKGND:
                WmEraseBkgnd(ref m);
                break;

            case PInvokeCore.WM_HELP:
                WmHelp(ref m);
                break;

            case PInvokeCore.WM_PAINT:
                if (GetStyle(ControlStyles.UserPaint))
                {
                    WmPaint(ref m);
                }
                else
                {
                    DefWndProc(ref m);
                }

                break;

            case PInvokeCore.WM_PRINTCLIENT:
                if (GetStyle(ControlStyles.UserPaint))
                {
                    WmPrintClient(ref m);
                }
                else
                {
                    DefWndProc(ref m);
                }

                break;

            case PInvokeCore.WM_SYSCOMMAND:
                if ((m.WParamInternal & 0xFFF0) == PInvoke.SC_KEYMENU && ToolStripManager.ProcessMenuKey(ref m))
                {
                    m.ResultInternal = (LRESULT)0;
                    return;
                }

                DefWndProc(ref m);
                break;

            case PInvokeCore.WM_INPUTLANGCHANGE:
                WmInputLangChange(ref m);
                break;

            case PInvokeCore.WM_INPUTLANGCHANGEREQUEST:
                WmInputLangChangeRequest(ref m);
                break;

            case PInvokeCore.WM_MEASUREITEM:
                if (m.WParamInternal != 0u)
                {
                    WmOwnerDraw(ref m);
                }

                break;

            case PInvokeCore.WM_SETCURSOR:
                WmSetCursor(ref m);
                break;

            case PInvokeCore.WM_WINDOWPOSCHANGING:
                WmWindowPosChanging(ref m);
                break;

            case PInvokeCore.WM_CHAR:
            case PInvokeCore.WM_KEYDOWN:
            case PInvokeCore.WM_SYSKEYDOWN:
            case PInvokeCore.WM_KEYUP:
            case PInvokeCore.WM_SYSKEYUP:
                WmKeyChar(ref m);
                break;

            case PInvokeCore.WM_CREATE:
                WmCreate(ref m);
                break;

            case PInvokeCore.WM_DESTROY:
                WmDestroy(ref m);
                break;

            case PInvokeCore.WM_CTLCOLOR:
            case PInvokeCore.WM_CTLCOLORBTN:
            case PInvokeCore.WM_CTLCOLORDLG:
            case PInvokeCore.WM_CTLCOLORMSGBOX:
            case PInvokeCore.WM_CTLCOLORSCROLLBAR:
            case PInvokeCore.WM_CTLCOLOREDIT:
            case PInvokeCore.WM_CTLCOLORLISTBOX:
            case PInvokeCore.WM_CTLCOLORSTATIC:

            // this is for the trinity guys. The case is if you've got a windows
            // forms edit or something hosted as an AX control somewhere, there isn't anyone to reflect
            // these back. If they went ahead and just sent them back, some controls don't like that
            // and end up recursing. Our code handles it fine because we just pick the HWND out of the LPARAM.
            case MessageId.WM_REFLECT_CTLCOLOR:
            case MessageId.WM_REFLECT_CTLCOLORBTN:
            case MessageId.WM_REFLECT_CTLCOLORDLG:
            case MessageId.WM_REFLECT_CTLCOLORMSGBOX:
            case MessageId.WM_REFLECT_CTLCOLORSCROLLBAR:
            case MessageId.WM_REFLECT_CTLCOLOREDIT:
            case MessageId.WM_REFLECT_CTLCOLORLISTBOX:
            case MessageId.WM_REFLECT_CTLCOLORSTATIC:
                WmCtlColorControl(ref m);
                break;

            case PInvokeCore.WM_HSCROLL:
            case PInvokeCore.WM_VSCROLL:
            case PInvokeCore.WM_DELETEITEM:
            case PInvokeCore.WM_VKEYTOITEM:
            case PInvokeCore.WM_CHARTOITEM:
            case PInvokeCore.WM_COMPAREITEM:
                if (!ReflectMessage(m.LParamInternal, ref m))
                {
                    DefWndProc(ref m);
                }

                break;

            case PInvokeCore.WM_IME_CHAR:
                WmImeChar(ref m);
                break;

            case PInvokeCore.WM_IME_STARTCOMPOSITION:
                WmImeStartComposition(ref m);
                break;

            case PInvokeCore.WM_IME_ENDCOMPOSITION:
                WmImeEndComposition(ref m);
                break;

            case PInvokeCore.WM_IME_NOTIFY:
                WmImeNotify(ref m);
                break;

            case PInvokeCore.WM_KILLFOCUS:
                WmKillFocus(ref m);
                break;

            case PInvokeCore.WM_LBUTTONDBLCLK:
                WmMouseDown(ref m, MouseButtons.Left, 2);
                if (GetStyle(ControlStyles.StandardDoubleClick))
                {
                    SetState(States.DoubleClickFired, true);
                }

                break;

            case PInvokeCore.WM_LBUTTONDOWN:
                WmMouseDown(ref m, MouseButtons.Left, 1);
                break;

            case PInvokeCore.WM_LBUTTONUP:
                WmMouseUp(ref m, MouseButtons.Left, 1);
                break;

            case PInvokeCore.WM_MBUTTONDBLCLK:
                WmMouseDown(ref m, MouseButtons.Middle, 2);
                if (GetStyle(ControlStyles.StandardDoubleClick))
                {
                    SetState(States.DoubleClickFired, true);
                }

                break;

            case PInvokeCore.WM_MBUTTONDOWN:
                WmMouseDown(ref m, MouseButtons.Middle, 1);
                break;

            case PInvokeCore.WM_MBUTTONUP:
                WmMouseUp(ref m, MouseButtons.Middle, 1);
                break;

            case PInvokeCore.WM_XBUTTONDOWN:
                WmMouseDown(ref m, GetXButton(m.WParamInternal.HIWORD), 1);
                break;

            case PInvokeCore.WM_XBUTTONUP:
                WmMouseUp(ref m, GetXButton(m.WParamInternal.HIWORD), 1);
                break;

            case PInvokeCore.WM_XBUTTONDBLCLK:
                WmMouseDown(ref m, GetXButton(m.WParamInternal.HIWORD), 2);
                if (GetStyle(ControlStyles.StandardDoubleClick))
                {
                    SetState(States.DoubleClickFired, true);
                }

                break;

            case PInvokeCore.WM_MOUSELEAVE:
                WmMouseLeave(ref m);
                break;

            case PInvokeCore.WM_DPICHANGED_BEFOREPARENT:
                WmDpiChangedBeforeParent(ref m);
                m.ResultInternal = (LRESULT)0;
                break;

            case PInvokeCore.WM_DPICHANGED_AFTERPARENT:
                WmDpiChangedAfterParent(ref m);
                m.ResultInternal = (LRESULT)0;
                break;

            case PInvokeCore.WM_MOUSEMOVE:
                WmMouseMove(ref m);
                break;

            case PInvokeCore.WM_MOUSEWHEEL:
                WmMouseWheel(ref m);
                break;

            case PInvokeCore.WM_MOVE:
                WmMove(ref m);
                break;

            case PInvokeCore.WM_NOTIFY:
                WmNotify(ref m);
                break;

            case PInvokeCore.WM_NOTIFYFORMAT:
                WmNotifyFormat(ref m);
                break;

            case MessageId.WM_REFLECT_NOTIFYFORMAT:
                m.ResultInternal = (LRESULT)(nint)PInvoke.NFR_UNICODE;
                break;

            case PInvokeCore.WM_SHOWWINDOW:
                WmShowWindow(ref m);
                break;

            case PInvokeCore.WM_RBUTTONDBLCLK:
                WmMouseDown(ref m, MouseButtons.Right, 2);
                if (GetStyle(ControlStyles.StandardDoubleClick))
                {
                    SetState(States.DoubleClickFired, true);
                }

                break;

            case PInvokeCore.WM_RBUTTONDOWN:
                WmMouseDown(ref m, MouseButtons.Right, 1);
                break;

            case PInvokeCore.WM_RBUTTONUP:
                WmMouseUp(ref m, MouseButtons.Right, 1);
                break;

            case PInvokeCore.WM_SETFOCUS:
                WmSetFocus(ref m);
                break;

            case PInvokeCore.WM_MOUSEHOVER:
                WmMouseHover(ref m);
                break;

            case PInvokeCore.WM_WINDOWPOSCHANGED:
                WmWindowPosChanged(ref m);
                break;

            case PInvokeCore.WM_QUERYNEWPALETTE:
                WmQueryNewPalette(ref m);
                break;

            case PInvokeCore.WM_UPDATEUISTATE:
                WmUpdateUIState(ref m);
                break;

            case PInvokeCore.WM_PARENTNOTIFY:
                WmParentNotify(ref m);
                break;

            case PInvokeCore.WM_SETTINGCHANGE:
                if (GetExtendedState(ExtendedStates.InterestedInUserPreferenceChanged) && GetTopLevel())
                {
                    SYSTEM_PARAMETERS_INFO_ACTION action = (SYSTEM_PARAMETERS_INFO_ACTION)(uint)m.WParamInternal;

                    // Left here for debugging purposes.
                    // string? text = m.LParamInternal == 0 ? null : new((char*)m.LParamInternal);

                    if (action is SYSTEM_PARAMETERS_INFO_ACTION.SPI_SETNONCLIENTMETRICS && m.LParamInternal == 0)
                    {
                        // Text scaling needs refreshed. This can happen when changing Accessibility->Text Size.
                        //
                        // SPI_SETNONCLIENTMETRICS is sent multiple times, once with no LParam, then twice with
                        // "WindowMetrics". Common controls listen to both SPI_SETNONCLIENTMETRICS and
                        // SPI_SETICONTITLELOGFONT. Waiting for SPI_SETICONTITLELOGFONT has some sort of timing issue
                        // where layout doesn't always update correctly.
                        //
                        // Historically we reset the font (s_defaultFont) on WM_SYSCOLORCHANGE, which does come through before any
                        // of the WM_SETTINGCHANGE messages. SPI_SETNONCLIENTMETRICS seems more correct.
                        //
                        // We don't want reset font if scaling not actually changed: https://github.com/dotnet/winforms/issues/11037
                        // Most (if not all) of these problems was solved with moving this code to SPI_SETNONCLIENTMETRICS!
                        // s_defaultFont = Application.DefaultFont ?? SystemFonts.MessageBoxFont;
                        // So we need to check both variants - manually set font (Application.DefaultFont is not null) and auto system font

                        if (Application.DefaultFont is null) // auto system font
                        {
                            if (s_defaultFont is not null) // we need to check only if s_defaultFont already set
                            {
                                Font font = SystemFonts.MessageBoxFont!;
                                if (!s_defaultFont.Equals(font)) // the font has changed
                                {
                                    s_defaultFont = font;
                                }
                                else
                                {
                                    font.Dispose();
                                }
                            }
                        }
                        else // manually set font
                        {
                            Application.ScaleDefaultFont(); // will update Application.s_defaultFont or Application.s_defaultFontScaled only if needed
                            s_defaultFont = Application.DefaultFont; // Application.s_defaultFontScaled ?? Application.s_defaultFont
                        }
                    }
                }

                break;

            case PInvokeCore.WM_SYSCOLORCHANGE:
                if (GetExtendedState(ExtendedStates.InterestedInUserPreferenceChanged) && GetTopLevel())
                {
                    OnSystemColorsChanged(EventArgs.Empty);
                }

                break;

            case PInvokeCore.WM_EXITMENULOOP:
            case PInvokeCore.WM_INITMENUPOPUP:
            case PInvokeCore.WM_MENUSELECT:
            default:

                // If we received a thread execute message, then execute it.
                if (m.Msg == (int)s_threadCallbackMessage && m.Msg != 0)
                {
                    InvokeMarshaledCallbacks();
                    return;
                }
                else if (m.Msg == (int)WM_GETCONTROLNAME)
                {
                    WmGetControlName(ref m);
                    return;
                }
                else if (m.Msg == (int)WM_GETCONTROLTYPE)
                {
                    WmGetControlType(ref m);
                    return;
                }

                if (m.MsgInternal == RegisteredMessage.WM_MOUSEENTER)
                {
                    WmMouseEnter(ref m);
                    break;
                }

                DefWndProc(ref m);
                break;
        }

        // Keep ourselves rooted until we're done processing the current message. While unlikely, it is possible
        // that we can lose the rooting for `this` and get finalized when it is no longer referenced as a local.
        GC.KeepAlive(this);
    }

    ArrangedElementCollection IArrangedElement.Children => ChildControls ?? ArrangedElementCollection.Empty;

    IArrangedElement? IArrangedElement.Container
    {
        get
        {
            // This is safe because the IArrangedElement interface is internal
            return ParentInternal;
        }
    }

    bool IArrangedElement.ParticipatesInLayout => GetState(States.Visible);

    void IArrangedElement.PerformLayout(IArrangedElement affectedElement, string? affectedProperty)
    {
        PerformLayout(new LayoutEventArgs(affectedElement, affectedProperty));
    }

    PropertyStore IArrangedElement.Properties
    {
        get { return Properties; }
    }

    // CAREFUL: This really calls SetBoundsCore, not SetBounds.
    void IArrangedElement.SetBounds(Rectangle bounds, BoundsSpecified specified)
    {
        ISite? site = Site;
        IComponentChangeService? changeService = null;
        PropertyDescriptor? sizeProperty = null;
        PropertyDescriptor? locationProperty = null;
        bool sizeChanged = false;
        bool locationChanged = false;

        if (site is not null && site.DesignMode && site.TryGetService(out changeService))
        {
            if (!AreDesignTimeFeaturesSupported)
            {
                throw new NotSupportedException(SR.DesignTimeFeaturesNotSupported);
            }

            sizeProperty = TypeDescriptor.GetProperties(this)[PropertyNames.Size];
            locationProperty = TypeDescriptor.GetProperties(this)[PropertyNames.Location];
            Debug.Assert(sizeProperty is not null && locationProperty is not null, "Error retrieving Size/Location properties on Control.");

            try
            {
                if (sizeProperty is not null && !sizeProperty.IsReadOnly && (bounds.Width != Width || bounds.Height != Height))
                {
                    if (site is not INestedSite)
                    {
                        changeService.OnComponentChanging(this, sizeProperty);
                    }

                    sizeChanged = true;
                }

                if (locationProperty is not null && !locationProperty.IsReadOnly && (bounds.X != _x || bounds.Y != _y))
                {
                    if (site is not INestedSite)
                    {
                        changeService.OnComponentChanging(this, locationProperty);
                    }

                    locationChanged = true;
                }
            }
            catch (InvalidOperationException)
            {
                // The component change events can throw InvalidOperationException if a change is
                // currently not allowed (typically because the doc data in VS is locked).
                // When this happens, we just eat the exception and proceed with the change.
            }
        }

        SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified);

        if (changeService is not null)
        {
            try
            {
                if (sizeChanged)
                {
                    changeService.OnComponentChanged(this, sizeProperty);
                }

                if (locationChanged)
                {
                    changeService.OnComponentChanged(this, locationProperty);
                }
            }
            catch (InvalidOperationException)
            {
                // The component change events can throw InvalidOperationException if a change is
                // currently not allowed (typically because the doc data in VS is locked).
                // When this happens, we just eat the exception and proceed with the change.
            }
        }
    }

    /// <summary>
    ///  Indicates whether or not the control supports UIA Providers via
    ///  IRawElementProviderFragment/IRawElementProviderFragmentRoot interfaces
    /// </summary>
    internal virtual bool SupportsUiaProviders => false;

    ///
    ///  Explicit support of DropTarget
    ///
    void IDropTarget.OnDragEnter(DragEventArgs drgEvent) => OnDragEnter(drgEvent);

    void IDropTarget.OnDragOver(DragEventArgs drgEvent) => OnDragOver(drgEvent);

    void IDropTarget.OnDragLeave(EventArgs e) => OnDragLeave(e);

    void IDropTarget.OnDragDrop(DragEventArgs drgEvent) => OnDragDrop(drgEvent);

    ///
    ///  Explicit support of DropSource
    ///
    void ISupportOleDropSource.OnGiveFeedback(GiveFeedbackEventArgs giveFeedbackEventArgs) => OnGiveFeedback(giveFeedbackEventArgs);

    void ISupportOleDropSource.OnQueryContinueDrag(QueryContinueDragEventArgs queryContinueDragEventArgs) => OnQueryContinueDrag(queryContinueDragEventArgs);

    #region IKeyboardToolTip implementation

    bool IKeyboardToolTip.CanShowToolTipsNow() =>
        IsHandleCreated && Visible && (ToolStripControlHost is not IKeyboardToolTip toolTip || toolTip.CanShowToolTipsNow());

    Rectangle IKeyboardToolTip.GetNativeScreenRectangle() => GetToolNativeScreenRectangle();

    IList<Rectangle> IKeyboardToolTip.GetNeighboringToolsRectangles() => GetNeighboringToolsRectangles();

    bool IKeyboardToolTip.IsHoveredWithMouse() => IsHoveredWithMouse();

    bool IKeyboardToolTip.HasRtlModeEnabled() =>
        TopLevelControlInternal is { } topLevelControl && topLevelControl.RightToLeft == RightToLeft.Yes && !IsMirrored;

    bool IKeyboardToolTip.AllowsToolTip() =>
        (ToolStripControlHost is not IKeyboardToolTip toolTip || toolTip.AllowsToolTip()) && AllowsKeyboardToolTip();

    IWin32Window IKeyboardToolTip.GetOwnerWindow() => this;

    void IKeyboardToolTip.OnHooked(ToolTip toolTip) => OnKeyboardToolTipHook(toolTip);

    void IKeyboardToolTip.OnUnhooked(ToolTip toolTip) => OnKeyboardToolTipUnhook(toolTip);

    string? IKeyboardToolTip.GetCaptionForTool(ToolTip toolTip) => GetCaptionForTool(toolTip);

    bool IKeyboardToolTip.ShowsOwnToolTip() =>
        (ToolStripControlHost is not IKeyboardToolTip toolTip || toolTip.ShowsOwnToolTip()) && ShowsOwnKeyboardToolTip();

    bool IKeyboardToolTip.IsBeingTabbedTo() => AreCommonNavigationalKeysDown();

    bool IKeyboardToolTip.AllowsChildrenToShowToolTips() => AllowsChildrenToShowToolTips();

    #endregion

    private IList<Rectangle> GetOwnNeighboringToolsRectangles()
    {
        Control? controlParent = ParentInternal;
        if (controlParent is null)
        {
            return Array.Empty<Rectangle>();
        }

        List<Rectangle> neighboringControlsRectangles = new(4);

        // Next and previous control which are accessible with Tab and Shift+Tab
        AddIfCreated(controlParent.GetNextSelectableControl(this, true, true, true, false));
        AddIfCreated(controlParent.GetNextSelectableControl(this, false, true, true, false));
        // Next and previous control which are accessible with arrow keys
        AddIfCreated(controlParent.GetNextSelectableControl(this, true, false, false, true));
        AddIfCreated(controlParent.GetNextSelectableControl(this, false, false, false, true));

        return neighboringControlsRectangles;

        void AddIfCreated(Control? control)
        {
            if (control?.IsHandleCreated ?? false)
            {
                neighboringControlsRectangles.Add(((IKeyboardToolTip)control).GetNativeScreenRectangle());
            }
        }
    }

    internal virtual bool ShowsOwnKeyboardToolTip() => true;

    internal virtual void OnKeyboardToolTipHook(ToolTip toolTip)
    {
    }

    internal virtual void OnKeyboardToolTipUnhook(ToolTip toolTip)
    {
    }

    internal virtual Rectangle GetToolNativeScreenRectangle()
    {
        PInvokeCore.GetWindowRect(this, out var rect);
        return rect;
    }

    internal virtual bool AllowsKeyboardToolTip()
    {
        // This internal method enables keyboard ToolTips for all controls including the foreign descendants of
        // Control unless this method is overridden in a child class belonging to this assembly. ElementHost is one
        // such control which is located in a different assembly.
        //
        // This control doesn't show a mouse ToolTip when hovered and thus should not have a keyboard ToolTip as
        // well. We are not going to fix it now since it seems unlikely that someone would set ToolTip on such
        // special container control as ElementHost.
        return true;
    }

    internal static unsafe bool AreCommonNavigationalKeysDown()
    {
        static bool IsKeyDown(Keys key, ReadOnlySpan<byte> stateArray)
            => (stateArray[(int)key] & HighOrderBitMask) != 0;

        ReadOnlySpan<byte> stateArray = stackalloc byte[256];

        fixed (byte* b = stateArray)
        {
            PInvoke.GetKeyboardState(b);
            return IsKeyDown(Keys.Tab, stateArray)
                || IsKeyDown(Keys.Up, stateArray)
                || IsKeyDown(Keys.Down, stateArray)
                || IsKeyDown(Keys.Left, stateArray)
                || IsKeyDown(Keys.Right, stateArray)
                // receiving focus from the ToolStrip
                || IsKeyDown(Keys.Menu, stateArray)
                || IsKeyDown(Keys.F10, stateArray)
                || IsKeyDown(Keys.Escape, stateArray);
        }
    }

    internal virtual ToolInfoWrapper<Control> GetToolInfoWrapper(TOOLTIP_FLAGS flags, string? caption, ToolTip tooltip)
        => new(this, flags, caption);

    private readonly WeakReference<ToolStripControlHost?> _toolStripControlHostReference = new(null);

    internal ToolStripControlHost? ToolStripControlHost
    {
        get
        {
            _toolStripControlHostReference.TryGetTarget(out ToolStripControlHost? value);
            return value;
        }
        set
        {
            _toolStripControlHostReference.SetTarget(value);
        }
    }

    HWND IHandle<HWND>.Handle => HWND;

    internal HWND HWND => (HWND)Handle;

    internal virtual bool AllowsChildrenToShowToolTips() => true;
}
