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

using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.Layout;

namespace System.Windows.Forms;

[Designer($"System.Windows.Forms.Design.ToolStripDropDownDesigner, {AssemblyRef.SystemDesign}")]
public partial class ToolStripDropDown : ToolStrip
{
    private ToolStripItem? _ownerItem;
    private bool _isAutoGenerated;
    private bool _dropShadowEnabled = true;
    private bool _autoClose = true;
    private bool _autoSize = true;
    private int _countDropDownItemsAssignedTo; // the number of dropdown items using this as their dropdown..
    private BitVector32 _state;
    private Point _displayLocation = new(0, 0);
    private bool _saveSourceControl;

    private ToolStripDropDownDirection _childDropDownDirection = ToolStripDropDownDirection.Default;
    private ToolStripDropDownCloseReason _closeReason = ToolStripDropDownCloseReason.AppFocusChange;

    private static readonly int s_propOpacity = PropertyStore.CreateKey();
    private static readonly int s_propSourceControl = PropertyStore.CreateKey();

    private static readonly object s_openingEvent = new();
    private static readonly object s_openedEvent = new();
    private static readonly object s_closedEvent = new();
    private static readonly object s_closingEvent = new();

    private static readonly Padding s_defaultPadding = new(1, 2, 1, 2);
    private Padding _scaledDefaultPadding = s_defaultPadding;

    private static readonly int s_stateLayered = BitVector32.CreateMask();
    private static readonly int s_stateAllowTransparency = BitVector32.CreateMask(s_stateLayered);
    private static readonly int s_stateNotWorkingAreaConstrained = BitVector32.CreateMask(s_stateAllowTransparency);
    private static readonly int s_stateInSetVisibleCore = BitVector32.CreateMask(s_stateNotWorkingAreaConstrained);

    public ToolStripDropDown()
    {
        _scaledDefaultPadding = ScaleHelper.ScaleToDpi(s_defaultPadding, ScaleHelper.InitialSystemDpi);
        SuspendLayout();
        Initialize();
        ResumeLayout(false);
    }

    internal ToolStripDropDown(ToolStripItem ownerItem) : this() => _ownerItem = ownerItem;

    internal ToolStripDropDown(ToolStripItem ownerItem, bool isAutoGenerated) : this(ownerItem) => _isAutoGenerated = isAutoGenerated;

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new bool AllowItemReorder
    {
        get => base.AllowItemReorder;
        set => base.AllowItemReorder = value;
    }

    /// <summary>
    ///  Gets or sets a value indicating whether the opacity of the form can be adjusted.
    /// </summary>
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [SRDescription(nameof(SR.ControlAllowTransparencyDescr))]
    public bool AllowTransparency
    {
        get => _state[s_stateAllowTransparency];
        set
        {
            if (value != (_state[s_stateAllowTransparency]))
            {
                _state[s_stateAllowTransparency] = value;

                _state[s_stateLayered] = _state[s_stateAllowTransparency];

                UpdateStyles();

                if (!value)
                {
                    // This sets it back to default, which is 1.0d.
                    Properties.RemoveValue(s_propOpacity);
                    UpdateLayered();
                }
            }
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public override AnchorStyles Anchor
    {
        get => base.Anchor;
        set => base.Anchor = value;
    }

    [DefaultValue(true)]
    public override bool AutoSize
    {
        get
        {
            return _autoSize;
        }
        set
        {
            if (_autoSize != value)
            {
                _autoSize = value;
                // we shadow CommonProperties
                LayoutTransaction.DoLayout(this, this, PropertyNames.AutoSize);
                OnAutoSizeChanged(EventArgs.Empty);
            }
        }
    }

    /// <summary>
    ///  Specifies whether the dropdown should automatically close when the dropdown has lost
    ///  activation. If you want a dropdown that always stays open, specify AutoClose = false;
    /// </summary>
    [DefaultValue(true)]
    [SRCategory(nameof(SR.CatBehavior))]
    [SRDescription(nameof(SR.ToolStripDropDownAutoCloseDescr))]
    public bool AutoClose
    {
        get => _autoClose;
        set
        {
            if (_autoClose != value)
            {
                _autoClose = value;
                ApplyTopMost(topMost: !_autoClose);
            }
        }
    }

    [Browsable(false)]
    public new event EventHandler? BackgroundImageChanged
    {
        add => base.BackgroundImageChanged += value;
        remove => base.BackgroundImageChanged -= value;
    }

    [Browsable(false)]
    public new event EventHandler? BackgroundImageLayoutChanged
    {
        add => base.BackgroundImageLayoutChanged += value;
        remove => base.BackgroundImageLayoutChanged -= value;
    }

    [Browsable(false)]
    public new event EventHandler? BindingContextChanged
    {
        add => base.BindingContextChanged += value;
        remove => base.BindingContextChanged -= value;
    }

    [DefaultValue(false)]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new bool CanOverflow
    {
        get => base.CanOverflow;
        set => base.CanOverflow = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event UICuesEventHandler? ChangeUICues
    {
        add => base.ChangeUICues += value;
        remove => base.ChangeUICues -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new ContextMenuStrip? ContextMenuStrip
    {
        get => base.ContextMenuStrip;
        set => base.ContextMenuStrip = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? ContextMenuStripChanged
    {
        add => base.ContextMenuStripChanged += value;
        remove => base.ContextMenuStripChanged -= value;
    }

    /// <summary>
    ///  This is called when creating a window. Inheriting classes can override
    ///  this to add extra functionality, but should not forget to first call
    ///  base.CreateParams() to make sure the control continues to work
    ///  correctly.
    /// </summary>
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;

            // If we're using themes then go ahead
            if (DropShadowEnabled)
            {
                cp.ClassStyle |= (int)WNDCLASS_STYLES.CS_DROPSHADOW;
            }

            // We're a borderless menuless control with no min/max boxes. We don't want to show in the taskbar either.

            // HOWTO: Prevent a Window from Appearing on the Taskbar
            // Give the window the WS_EX_TOOLWINDOW extended style, and remove the WS_EX_APPWINDOW style. As a side
            // effect, the window will have a smaller caption than a normal window.

            // Give the window the WS_POPUP style and make it owned by a hidden window. (Form)

            // No caption, no siblings.
            cp.Style &= ~(int)(WINDOW_STYLE.WS_CAPTION | WINDOW_STYLE.WS_CLIPSIBLINGS);
            // Don't show in the taskbar
            cp.ExStyle &= ~(int)WINDOW_EX_STYLE.WS_EX_APPWINDOW;
            cp.Style |= TopLevel ? unchecked((int)WINDOW_STYLE.WS_POPUP) : (int)WINDOW_STYLE.WS_CHILD;
            cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_CONTROLPARENT;

            bool topLevel = TopLevel;

            // opacity
            if (topLevel && _state[s_stateLayered])
            {
                cp.ExStyle |= (int)WINDOW_EX_STYLE.WS_EX_LAYERED;
            }
            else if (topLevel)
            {
                // From MSDN: Menus, dialog boxes, and combo list boxes have the CS_SAVEBITS style.
                // When you use this style for a window,Windows saves a bitmap copy of the screen image that the
                // window obscures. First, Windows asks the display driver to save the bits. If the display driver
                // has enough memory, it saves the bits for Windows. If the display driver does not have enough memory,
                // Window saves the bits itself as a bitmap in global memory and also uses some of User's local heap
                // for housekeeping structures for each window. When the application removes the window,
                // Windows can restore the screen image quickly by using the stored bits.
                cp.ClassStyle |= (int)WNDCLASS_STYLES.CS_SAVEBITS;
            }
            else if (!topLevel)
            {
                cp.Style |= (int)WINDOW_STYLE.WS_CLIPSIBLINGS;
            }

            // We're turning off CLIPSIBLINGS because in the designer the elements of the form beneath
            // are actually sibling controls. We want to paint right over them as if we were a toplevel window.

            return cp;
        }
    }

    protected override Padding DefaultPadding
    {
        get { return _scaledDefaultPadding; }
    }

    /// <summary> We want this to default to true... This way tooltips on overflows and custom dropdowns will show.
    ///  Since menu items don't show tooltips by default we can safely leave it on </summary>
    protected override bool DefaultShowItemToolTips
    {
        get
        {
            return true;
        }
    }

    protected override DockStyle DefaultDock => DockStyle.None;

    public override ToolStripDropDownDirection DefaultDropDownDirection
    {
        get => _childDropDownDirection == ToolStripDropDownDirection.Default
            ? (RightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right
            : _childDropDownDirection;
        set
        {
            _childDropDownDirection = value;
            base.DefaultDropDownDirection = value;
        }
    }

    [DefaultValue(DockStyle.None)]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public override DockStyle Dock
    {
        get => base.Dock;
        set => base.Dock = value;
    }

    ///  changed the browsable attribute
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? DockChanged
    {
        add => base.DockChanged += value;
        remove => base.DockChanged -= value;
    }

    internal override NativeWindow DropDownOwnerWindow =>
        // Re-use the drop down owner from our parenting tool strip if we can.
        _ownerItem?.Owner is not null ? _ownerItem.Owner.DropDownOwnerWindow : base.DropDownOwnerWindow;

    public bool DropShadowEnabled
    {
        get
        {
            // DropShadows are only supported on TopMost windows
            // due to the flakiness of the way it's implemented in the OS. (Non toplevel
            // windows can have parts of the shadow disappear because another window can get
            // sandwiched between the SysShadow window and the dropdown.)
            return _dropShadowEnabled && TopMost && DisplayInformation.IsDropShadowEnabled;
        }
        set
        {
            if (_dropShadowEnabled != value)
            {
                _dropShadowEnabled = value;
                if (IsHandleCreated && !DesignMode)
                {
                    RecreateHandle();
                }
            }
        }
    }

    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ToolStripDropDownClosedDecr))]
    public event ToolStripDropDownClosedEventHandler? Closed
    {
        add => Events.AddHandler(s_closedEvent, value);
        remove => Events.RemoveHandler(s_closedEvent, value);
    }

    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ToolStripDropDownClosingDecr))]
    public event ToolStripDropDownClosingEventHandler? Closing
    {
        add => Events.AddHandler(s_closingEvent, value);
        remove => Events.RemoveHandler(s_closingEvent, value);
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? Enter
    {
        add => base.Enter += value;
        remove => base.Enter -= value;
    }

    [AllowNull]
    public override Font Font
    {
        get
        {
            if (IsFontSet())
            {
                return base.Font;
            }

            // if the FONT isn't set, then return our owner item's font.
            return IsAutoGenerated && OwnerItem is not null ? OwnerItem.Font : base.Font;
        }
        set => base.Font = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? FontChanged
    {
        add => base.FontChanged += value;
        remove => base.FontChanged -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? ForeColorChanged
    {
        add => base.ForeColorChanged += value;
        remove => base.ForeColorChanged -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event GiveFeedbackEventHandler? GiveFeedback
    {
        add => base.GiveFeedback += value;
        remove => base.GiveFeedback -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new ToolStripGripDisplayStyle GripDisplayStyle
    {
        get => base.GripDisplayStyle;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new Rectangle GripRectangle
    {
        get => base.GripRectangle;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new Padding GripMargin
    {
        get => base.GripMargin;
        set => base.GripMargin = value;
    }

    [DefaultValue(ToolStripGripStyle.Hidden)]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new ToolStripGripStyle GripStyle
    {
        get => base.GripStyle;
        set => base.GripStyle = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event HelpEventHandler? HelpRequested
    {
        add => base.HelpRequested += value;
        remove => base.HelpRequested -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? ImeModeChanged
    {
        add => base.ImeModeChanged += value;
        remove => base.ImeModeChanged -= value;
    }

    /// <summary>
    ///  determines whether this dropdown was autogenerated.
    /// </summary>
    [Browsable(false)]
    public bool IsAutoGenerated
    {
        get
        {
            return _isAutoGenerated;
        }
    }

    internal bool IsAssignedToDropDownItem
    {
        get
        {
            return IsAutoGenerated || (_countDropDownItemsAssignedTo > 0);
        }
    }

    internal override Size ImageScalingSizeInternal
    {
        get => IsAutoGenerated && OwnerToolStrip is not null
            ? OwnerToolStrip.ImageScalingSizeInternal
            : base.ImageScalingSizeInternal;
        set => base.ImageScalingSizeInternal = value;
    }

    internal override bool KeyboardActive
    {
        // Ripple up the chain until we get the topmost toolstrip (usually the main menu strip is the one we care about)
        get => OwnerToolStrip is { } ownerToolStrip ? ownerToolStrip.KeyboardActive : base.KeyboardActive;
        set
        {
            base.KeyboardActive = value;

            if (OwnerToolStrip is { } ownerToolStrip)
            {
                ownerToolStrip.KeyboardActive = value;
            }
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event KeyEventHandler? KeyDown
    {
        add => base.KeyDown += value;
        remove => base.KeyDown -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event KeyPressEventHandler? KeyPress
    {
        add => base.KeyPress += value;
        remove => base.KeyPress -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event KeyEventHandler? KeyUp
    {
        add => base.KeyUp += value;
        remove => base.KeyUp -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? Leave
    {
        add => base.Leave += value;
        remove => base.Leave -= value;
    }

    /// <summary>
    ///  Override Location to make it hidden from the user in the designer
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public new Point Location
    {
        get => base.Location;
        set => base.Location = value;
    }

    [SRCategory(nameof(SR.CatAction))]
    [SRDescription(nameof(SR.ToolStripDropDownOpeningDescr))]
    public event CancelEventHandler? Opening
    {
        add => Events.AddHandler(s_openingEvent, value);
        remove => Events.RemoveHandler(s_openingEvent, value);
    }

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

    protected internal override Size MaxItemSize
    {
        get
        {
            return Screen.GetWorkingArea(Bounds).Size - Padding.Size;
        }
    }

    /// <summary>
    ///  Determines the opacity of the form. This can only be set on top level controls.
    ///  Opacity requires Windows 2000 or later, and is ignored on earlier operating systems.
    /// </summary>
    [SRCategory(nameof(SR.CatWindowStyle))]
    [TypeConverter(typeof(OpacityConverter))]
    [SRDescription(nameof(SR.FormOpacityDescr))]
    [DefaultValue(1.0)]
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public double Opacity
    {
        get => Properties.GetValueOrDefault(s_propOpacity, 1.0d);
        set
        {
            value = Math.Clamp(value, 0.0d, 1.0d);

            Properties.AddOrRemoveValue(s_propOpacity, value, 1.0d);

            bool oldLayered = _state[s_stateLayered];
            if (OpacityAsByte < 255)
            {
                AllowTransparency = true;
                _state[s_stateLayered] = true;
            }
            else
            {
                _state[s_stateLayered] = false;
            }

            if (oldLayered != _state[s_stateLayered])
            {
                UpdateStyles();
            }

            UpdateLayered();
        }
    }

    private byte OpacityAsByte => (byte)(Opacity * 255.0f);

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new ToolStripOverflowButton OverflowButton => base.OverflowButton;

    [DefaultValue(null)]
    [Browsable(false)]
    public ToolStripItem? OwnerItem
    {
        get => _ownerItem;
        set
        {
            if (_ownerItem != value)
            {
                Font originalFont = Font;
                RightToLeft startRightToLeft = RightToLeft;
                _ownerItem = value;
                // RESET ambient properties
                if (!originalFont.Equals(Font))
                {
                    OnOwnerItemFontChanged(EventArgs.Empty);
                }

                if (_ownerItem is not null && RightToLeftInherited && startRightToLeft != RightToLeft)
                {
                    using (new LayoutTransaction(this, this, PropertyNames.RightToLeft))
                    {
                        OnRightToLeftChanged(EventArgs.Empty);
                    }
                }
            }
        }
    }

    internal ToolStripDropDownItem? OwnerDropDownItem
    {
        get { return OwnerItem as ToolStripDropDownItem; }
    }

    internal ToolStrip? OwnerToolStrip
    {
        get
        {
            if (_ownerItem is not null)
            {
                ToolStrip? owner = _ownerItem.ParentInternal;
                if (owner is not null)
                {
                    return owner;
                }

                // might not actually be placed on the overflow, just check for sure.
                if (_ownerItem.Placement == ToolStripItemPlacement.Overflow && _ownerItem.Owner is not null)
                {
                    return _ownerItem.Owner.OverflowButton.DropDown;
                }

                if (owner is null)
                {
                    return _ownerItem.Owner;
                }
            }

            return null;
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new Region? Region
    {
        get => base.Region;
        set => base.Region = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? RegionChanged
    {
        add => base.RegionChanged += value;
        remove => base.RegionChanged -= value;
    }

    internal virtual bool RequiresScrollButtons
    {
        get
        {
            return false;
        }
        set
        {
            Debug.Fail("You can't set this except on ToolStripDropDownMenu");
        }
    }

    [SRCategory(nameof(SR.CatAppearance))]
    [Localizable(true)]
    [AmbientValue(RightToLeft.Inherit)]
    [SRDescription(nameof(SR.ControlRightToLeftDescr))]
    public override RightToLeft RightToLeft
    {
        get
        {
            // our inheritance is from our owner item.
            if (RightToLeftInherited)
            {
                if (SourceControlInternal is not null)
                {
                    return SourceControlInternal.RightToLeft;
                }

                if (OwnerItem is not null)
                {
                    return OwnerItem.RightToLeft;
                }
            }

            return base.RightToLeft;
        }
        set => base.RightToLeft = value;
    }

    private bool RightToLeftInherited
    {
        get
        {
            // fish out of control's property store whether or not RTL was set, if it's set to inherit.
            return !ShouldSerializeRightToLeft();
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event ScrollEventHandler? Scroll
    {
        add => base.Scroll += value;
        remove => base.Scroll -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new bool Stretch
    {
        get => base.Stretch;
        set => base.Stretch = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    public new event EventHandler? StyleChanged
    {
        add => base.StyleChanged += value;
        remove => base.StyleChanged -= value;
    }

    internal Control? SourceControlInternal
    {
        get => Properties.GetValueOrDefault<Control>(s_propSourceControl);
        set => Properties.AddOrRemoveValue(s_propSourceControl, value);
    }

    internal override SHOW_WINDOW_CMD ShowParams => SHOW_WINDOW_CMD.SW_SHOWNOACTIVATE;

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? TabStopChanged
    {
        add => base.TabStopChanged += value;
        remove => base.TabStopChanged -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? TextChanged
    {
        add => base.TextChanged += value;
        remove => base.TextChanged -= value;
    }

    [Browsable(false)]
    [DefaultValue(ToolStripTextDirection.Horizontal)]
    [SRDescription(nameof(SR.ToolStripTextDirectionDescr))]
    [SRCategory(nameof(SR.CatAppearance))]
    public override ToolStripTextDirection TextDirection
    {
        get => base.TextDirection;
        set => base.TextDirection = value;
    }

    // Consistency: match casing of Form.TopMost, which shipped in Everett, even though it's wrong
    protected virtual bool TopMost
    {
        get { return true; }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public bool TopLevel
    {
        get
        {
            return GetTopLevel();
        }
        set
        {
            //
            if (value != GetTopLevel())
            {
                SetTopLevelInternal(value);
                SetTopLevelStyle(value);
            }
        }
    }

    // public Color TransparencyKey {
    //    This property intentionally not available for ToolStripDropDown
    //    it's not robust enough for our needs
    // }

    ///  Override base TabIndex property in order to avoid serialization
    ///  (since a dropdown shouldn't participate in the taborder...)
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public new int TabIndex
    {
        get => base.TabIndex;
        set => base.TabIndex = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [SRCategory(nameof(SR.CatPropertyChanged))]
    [SRDescription(nameof(SR.ControlOnTabIndexChangedDescr))]
    public new event EventHandler? TabIndexChanged
    {
        add => base.TabIndexChanged += value;
        remove => base.TabIndexChanged -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? Validated
    {
        add => base.Validated += value;
        remove => base.Validated -= value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event CancelEventHandler? Validating
    {
        add => base.Validating += value;
        remove => base.Validating -= value;
    }

    ///  Override base Visible property in order to control serialization by setting default value
    [SRCategory(nameof(SR.CatBehavior))]
    [Localizable(true)]
    [SRDescription(nameof(SR.ControlVisibleDescr))]
    [DefaultValue(false)]
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new bool Visible
    {
        get => base.Visible;
        set => base.Visible = value;
    }

    // internally we store the negated value so we don't have to initialize it.
    internal bool WorkingAreaConstrained
    {
        get => !_state[s_stateNotWorkingAreaConstrained];
        set => _state[s_stateNotWorkingAreaConstrained] = !value;
    }

    internal void AssignToDropDownItem()
    {
        _countDropDownItemsAssignedTo++;
    }

    internal void AdjustSize()
    {
        Size size = GetSuggestedSize();
        if (size != Size)
        {
            Size = size;
        }
    }

    private void ApplyTopMost(bool topMost)
    {
        if (TopMost)
        {
            PInvoke.SetWindowPos(
                this,
                topMost ? HWND.HWND_TOPMOST : HWND.HWND_NOTOPMOST,
                0, 0, 0, 0,
                SET_WINDOW_POS_FLAGS.SWP_NOMOVE | SET_WINDOW_POS_FLAGS.SWP_NOSIZE | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            SourceControlInternal = null;
        }

        base.Dispose(disposing);
    }

    private void CancelAutoExpand()
    {
        ToolStrip? toplevelOwnerToolStrip = GetToplevelOwnerToolStrip();
        if (toplevelOwnerToolStrip is not null)
        {
            toplevelOwnerToolStrip.MenuAutoExpand = false;
        }
    }

    internal override bool CanProcessMnemonic() =>
        // Don't let mnemonics act as keyboard input in IE in the internet.
        Application.MessageLoop && base.CanProcessMnemonic();

    protected override AccessibleObject CreateAccessibilityInstance() => new ToolStripDropDownAccessibleObject(this);

    protected override LayoutSettings? CreateLayoutSettings(ToolStripLayoutStyle style)
    {
        LayoutSettings? layoutSettings = base.CreateLayoutSettings(style);

        if (style == ToolStripLayoutStyle.Flow)
        {
            FlowLayoutSettings flowLayoutSettings = (FlowLayoutSettings)layoutSettings!;
            flowLayoutSettings.FlowDirection = FlowDirection.TopDown;
            flowLayoutSettings.WrapContents = false;
            return flowLayoutSettings;
        }

        return layoutSettings;
    }

    protected override void CreateHandle()
    {
        base.CreateHandle();

        if (TopLevel)
        {
            ReparentToDropDownOwnerWindow();

            if (!AutoClose || !WorkingAreaConstrained)
            {
                ApplyTopMost(true);
            }
        }

        if (DesignMode)
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, false);
        }
    }

    public void Close()
    {
        SetCloseReason(ToolStripDropDownCloseReason.CloseCalled);
        Visible = false;
        // we were the last one in the chain, roll out of menu mode.
        if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() is null)
        {
            ToolStripManager.ModalMenuFilter.ExitMenuMode();
        }
    }

    private void ResetCloseReason()
    {
        _closeReason = ToolStripDropDownCloseReason.AppFocusChange;
    }

    internal void SetCloseReason(ToolStripDropDownCloseReason reason)
    {
        _closeReason = reason;
    }

    public void Close(ToolStripDropDownCloseReason reason)
    {
        SetCloseReason(reason);
        Visible = false;
    }

    internal Rectangle GetDropDownBounds(Rectangle suggestedBounds)
    {
        Rectangle dropDownBounds;
        if (TopLevel)
        {
            Point screenPoint = _ownerItem is ToolStripDropDownItem dropDownItem
                ? dropDownItem.DropDownLocation
                : suggestedBounds.Location;

            Rectangle suggestedScreenBounds = new(screenPoint, suggestedBounds.Size);
            dropDownBounds = WorkingAreaConstrained
                ? WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(suggestedScreenBounds)
                : WindowsFormsUtils.ConstrainToScreenBounds(suggestedScreenBounds);
        }
        else
        {
            Point parentClientPoint = (_ownerItem is ToolStripDropDownItem dropDownItem) && (ParentInternal is not null)
                ? ParentInternal.PointToClient(dropDownItem.DropDownLocation)
                : suggestedBounds.Location;

            dropDownBounds = new Rectangle(parentClientPoint, suggestedBounds.Size);
        }

        return dropDownBounds;
    }

    internal Rectangle CalculateDropDownLocation(Point start, ToolStripDropDownDirection dropDownDirection)
    {
        Point offset = Point.Empty;
        if (!IsHandleCreated)
        {
            // PERF:
            // if the handle isn't created yet, then we likely haven't performed layout
            // yet. force a layout here so that we get the correct size.
            LayoutTransaction.DoLayout(this, this, PropertyNames.PreferredSize);
        }

        Rectangle dropDownBounds = new(Point.Empty, GetSuggestedSize());
        // calculate the offset from the upper left hand corner of the item.
        switch (dropDownDirection)
        {
            case ToolStripDropDownDirection.AboveLeft:
                offset.X = -dropDownBounds.Width;
                offset.Y = -dropDownBounds.Height;
                break;
            case ToolStripDropDownDirection.AboveRight:
                offset.Y = -dropDownBounds.Height;
                break;
            case ToolStripDropDownDirection.BelowRight:
            case ToolStripDropDownDirection.Right:
                break;
            case ToolStripDropDownDirection.BelowLeft:
            case ToolStripDropDownDirection.Left:
                offset.X = -dropDownBounds.Width;
                break;
        }

        dropDownBounds.Location = new Point(start.X + offset.X, start.Y + offset.Y);
        if (WorkingAreaConstrained)
        {
            dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds);
        }

        return dropDownBounds;
    }

    internal Size GetSuggestedSize() => AutoSize ? GetPreferredSize(Size.Empty) : Size;

    /// <summary>
    ///  Returns the ToolStrip from which all the dropdowns started from. This can be null.
    /// </summary>
    internal override ToolStrip? GetToplevelOwnerToolStrip()
    {
        ToolStripDropDown topmost = GetFirstDropDown();
        return (topmost.OwnerItem is null) ? null : topmost.OwnerToolStrip;
    }

    internal ToolStripItem? GetToplevelOwnerItem()
    {
        ToolStripDropDown topmost = GetFirstDropDown();
        return topmost.OwnerItem;
    }

    internal override void HandleItemClicked(ToolStripItem dismissingItem)
    {
        // Only clear the SourceControl if this is the last click.
        if (ActiveDropDowns.Count == 0)
        {
            // post processing after the click has happened.
            SourceControlInternal = null;
        }

        base.HandleItemClicked(dismissingItem);
    }

    /// <summary>
    ///  Set some common properties
    /// </summary>
    internal virtual void Initialize()
    {
        SetState(States.Visible, false);
        SetTopLevelInternal(true);

        // Marking this as a modal form prevents it from being activated
        // by the IMsoComponentManager, which will break keyboard routing in VS.
        SetState(States.Modal, true);

        SetStyle(ControlStyles.ResizeRedraw, true);
        UpdateStyles();
        GripStyle = ToolStripGripStyle.Hidden;
        CanOverflow = false;
        LayoutStyle = ToolStripLayoutStyle.Flow;
        MenuAutoExpand = true;
        AutoSize = true;
    }

    protected virtual void OnClosed(ToolStripDropDownClosedEventArgs e)
    {
        ((ToolStripDropDownClosedEventHandler?)Events[s_closedEvent])?.Invoke(this, e);
    }

    protected virtual void OnClosing(ToolStripDropDownClosingEventArgs e)
    {
        ((ToolStripDropDownClosingEventHandler?)Events[s_closingEvent])?.Invoke(this, e);
    }

    /// <summary>
    ///  When our handle is being created, suspend the deactivation
    ///  portion of the WndProc, as we'll never be shown.
    /// </summary>
    protected override void OnHandleCreated(EventArgs e)
    {
        UpdateStyles();  // get rid of WS_CAPTION style
        base.OnHandleCreated(e);
        UpdateLayered(); // update transparency
    }

    protected override void OnItemClicked(ToolStripItemClickedEventArgs e)
    {
        try
        {
            base.OnItemClicked(e);
        }
        finally
        {
            if (AutoClose)
            {
                if ((!(e.ClickedItem is ToolStripDropDownItem dismissingItem))                   // it's not a dropdownitem
                   || (dismissingItem is ToolStripSplitButton && !dismissingItem.DropDown.Visible) // clicking on the split button button dismisses
                   || !dismissingItem.HasDropDownItems)
                {   // clicking on a item w/dropdown does not dismiss window
                    Close(ToolStripDropDownCloseReason.ItemClicked);
                }
            }
        }
    }

    protected override void OnLayout(LayoutEventArgs e)
    {
        // It's important to size the dropdown first, then layout so that
        // the layout engine and SetDisplayedItems know how big the container is.
        AdjustSize();
        base.OnLayout(e);
    }

    protected virtual void OnOpening(CancelEventArgs e)
    {
        ((CancelEventHandler?)Events[s_openingEvent])?.Invoke(this, e);
    }

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

    protected override void OnVisibleChanged(EventArgs e)
    {
        if (Location != _displayLocation)
        {
            // If we adjusted the position from where the user wanted it,
            // see if we can put it in the right location now that they've changed
            // the items collection, and store where we actually have it.

            // Just because this is the case doesn't mean that we need to do another
            // another layout however.
            Location = _displayLocation;
            _displayLocation = Location;
        }

        if (AutoScroll || LayoutRequired)
        {
            // the base here forces a layout... we really only want to do this the first
            // time we pop the window... the subsequent times should be snappy.
            base.OnVisibleChanged(e);
        }
        else
        {
            SuspendLayout();
            try
            {
                // scrollable control forces a layout here for scrollbar reasons only
                // since we toggle visibility a lot this is expensive. Let's be clever and
                // not do it.
                base.OnVisibleChanged(e);
            }
            finally
            {
                ResumeLayout(false);
            }
        }
    }

    protected override void OnParentChanged(EventArgs e)
    {
        base.OnParentChanged(e);
        Rectangle bounds = Bounds;
        SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, BoundsSpecified.Location);
    }

    protected override void OnMouseUp(MouseEventArgs mea)
    {
        base.OnMouseUp(mea);

        // Menus should dismiss when you drag off
        if (!ClientRectangle.Contains(mea.Location))
        {
            bool dismiss = true;
            if (OwnerToolStrip is not null && OwnerItem is not null)
            {
                if (OwnerItem.Bounds.Contains(WindowsFormsUtils.TranslatePoint(mea.Location, this, OwnerToolStrip)))
                {
                    dismiss = false;  // don't dismiss if we clicked on our owner item
                }
            }

            if (dismiss)
            {
                DismissAll();
                CancelAutoExpand();
            }
        }
    }

    internal void OnOwnerItemFontChanged(EventArgs e)
    {
        if (IsAutoGenerated && OwnerItem is not null)
        {
            using (new LayoutTransaction(this, OwnerItem, PropertyNames.Font))
            {
                OnFontChanged(e);
            }
        }
    }

    internal void SelectPreviousToolStrip()
    {
        // snap the owner item before calling hide as non-auto created dropdowns will
        // exit menu mode if there's no OwnerItem.
        ToolStripItem? itemOnPreviousMenuToSelect = OwnerItem;
        Hide();

        if (itemOnPreviousMenuToSelect is not null)
        {
            itemOnPreviousMenuToSelect.Select(forceRaiseAccessibilityFocusChanged: true);

            KeyboardToolTipStateMachine.Instance.NotifyAboutGotFocus(itemOnPreviousMenuToSelect);

            if (OwnerToolStrip is not null)
            {
                // make sure we send keyboard handling where we've just
                // sent selection
                if (!OwnerToolStrip.IsDropDown)
                {
                    if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() != OwnerToolStrip)
                    {
                        ToolStripManager.ModalMenuFilter.SetActiveToolStrip(OwnerToolStrip);
                    }

                    // escape should cancel auto expansion
                    OwnerToolStrip.MenuAutoExpand = false;
                    // When the control cannot be select (TabStop), we can press "Tab" to
                    // navigate inside the owner toolstrip. Otherwise, press "Tab" will leave
                    // the owner toolstrip so it should exit the menu mode.
                    if (OwnerToolStrip.CanSelect)
                    {
                        ToolStripManager.ModalMenuFilter.ExitMenuMode();
                    }
                }
            }
        }
        else
        {
            ToolStripManager.ModalMenuFilter.ExitMenuMode();
        }
    }

    /// <summary>
    ///  this is where we handle navigation between the File,Edit,View dropdowns
    ///  if you have one of these dropdowns showing and you hit the arrow key
    ///  and it's not processed by the menu item
    ///  </summary>
    internal override bool ProcessArrowKey(Keys keyCode)
    {
        ToolStripMenuItem.MenuTimer.Cancel();

        if (keyCode is Keys.Left or Keys.Right)
        {
            bool forward = keyCode == Keys.Right;

            if (!IsFirstDropDown && !forward)
            {
                // this is the case where you've cascaded out to a second level dropdown and you hit the back arrow
                // key. In this case we want to just hide the current dropdown
                Visible = false;
                return true;
            }
            else
            {
                bool closeOnHorizontalKey = false;
                if (LayoutStyle == ToolStripLayoutStyle.Flow)
                {
                    closeOnHorizontalKey = FlowLayout.GetFlowDirection(this) == FlowDirection.TopDown && !FlowLayout.GetWrapContents(this);
                }

                if (closeOnHorizontalKey)
                {
                    ToolStrip? toplevelToolStrip = GetToplevelOwnerToolStrip();
                    ToolStripItem? rootItem = GetToplevelOwnerItem();

                    // This is the case where you need to open up the adjacent DropDown (File->Edit) menus because:
                    //      - this is the toplevel ToolStripDropDown and you hit left or right
                    //      - this is a non-toplevel ToolStripDropDown and you hit an arrow key in a direction
                    //        of the cascade AND the current item has no cascading menu associated with it.

                    bool isOnOverflow = (OwnerItem is not null && OwnerItem.IsOnOverflow);
                    if (forward || !isOnOverflow)
                    {
                        SetCloseReason(ToolStripDropDownCloseReason.Keyboard);
                        DismissAll();
                    }
                    else if (isOnOverflow)
                    {
                        // Going backwards should roll us up and our children but not the overflow.
                        Visible = false;
                    }

                    if (toplevelToolStrip is not null && rootItem is not null)
                    {
                        if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() != toplevelToolStrip)
                        {
                            ToolStripManager.ModalMenuFilter.SetActiveToolStrip(toplevelToolStrip);
                        }

                        toplevelToolStrip.SelectNextToolStripItem(rootItem, forward);
                    }

                    return true;
                }
            }
        }

        // get base behavior like up/down navigation.
        return base.ProcessArrowKey(keyCode);
    }

    protected override bool ProcessDialogKey(Keys keyData)
    {
        if (OwnerItem is not null && OwnerItem.IsInDesignMode)
        {
            return false;
        }

        if (AutoClose && Visible & ToolStripManager.IsMenuKey(keyData))
        {
            SetCloseReason(ToolStripDropDownCloseReason.Keyboard);
            DismissAll();
            ToolStrip? toplevel = GetToplevelOwnerToolStrip();
            if (toplevel is not null)
            {
                toplevel.RestoreFocusInternal();
                ToolStripManager.ModalMenuFilter.MenuKeyToggle = true;
            }

            ToolStripManager.ModalMenuFilter.ExitMenuMode();

            return true;
        }

        if ((keyData & Keys.KeyCode) == Keys.Escape)
        {
            SetCloseReason(ToolStripDropDownCloseReason.Keyboard);
            SelectPreviousToolStrip();

            return true;
        }

        return base.ProcessDialogKey(keyData);
    }

    [EditorBrowsable(EditorBrowsableState.Advanced)]
    protected override bool ProcessDialogChar(char charCode) =>
        // Since we're toplevel and aren't a container control, we've got to do our own mnemonic handling.
        ((OwnerItem is null || OwnerItem.Pressed) && charCode != ' ' && ProcessMnemonic(charCode))
            || base.ProcessDialogChar(charCode);

    protected internal override bool ProcessMnemonic(char charCode) =>
        CanProcessMnemonic() && base.ProcessMnemonic(charCode);

    internal override void ProcessDuplicateMnemonic(ToolStripItem item, char charCode)
    {
        if (!CanProcessMnemonic())
        {
            return;
        }

        if (item is not null)
        {
            base.ProcessDuplicateMnemonic(item, charCode);
        }
    }

    internal override void RecreateHandleCore()
    {
        // If we're visible, then we'll have set our parent hwnd to the active control.
        // That means that re-create handle will set it as our parent, but that's not what
        // we want, since that means that from now on we'll be displayed in that controls
        // client co-ordinates. To fix this, we first re-parent ourselves back to the
        // hidden window, do the re-create, then set the parent again.
        if (Visible)
        {
            ReparentToDropDownOwnerWindow();
        }

        base.RecreateHandleCore();
        if (Visible)
        {
            ReparentToActiveToolStripWindow();
        }
    }

    private void ResetDropShadowEnabled()
    {
        DropShadowEnabled = true;
    }

    private void ReparentToActiveToolStripWindow()
    {
        ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this);
        PInvokeCore.SetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_HWNDPARENT, ToolStripManager.ModalMenuFilter.ActiveHwnd);
    }

    private void ReparentToDropDownOwnerWindow()
    {
        // when we're toplevel we need to parent ourselves to a hidden window
        // this prevents a taskbar entry.
        PInvokeCore.SetWindowLong(this, WINDOW_LONG_PTR_INDEX.GWL_HWNDPARENT, DropDownOwnerWindow);
    }

    internal override void ResetScaling(int newDpi)
    {
        base.ResetScaling(newDpi);
        CommonProperties.xClearPreferredSizeCache(this);
        _scaledDefaultPadding = ScaleHelper.ScaleToDpi(s_defaultPadding, newDpi);
    }

    /// <summary>
    ///     VERY similar to Form.ScaleCore
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    protected override void ScaleCore(float dx, float dy)
    {
        SuspendLayout();

        try
        {
            // Get size values in advance to prevent one change from affecting another.
            Size clientSize = ClientSize;
            Size minSize = MinimumSize;
            Size maxSize = MaximumSize;
            ClientSize = ScaleSize(clientSize, dx, dy);
            if (!MinimumSize.IsEmpty)
            {
                MinimumSize = ScaleSize(minSize, dx, dy);
            }

            if (!MaximumSize.IsEmpty)
            {
                MaximumSize = ScaleSize(maxSize, dx, dy);
            }

            ScaleDockPadding(dx, dy);

            foreach (Control control in Controls)
            {
#pragma warning disable CS0618 // Type or member is obsolete - compat
                control?.Scale(dx, dy);
#pragma warning restore CS0618
            }
        }
        finally
        {
            ResumeLayout();
        }
    }

    /// <summary>
    ///  Scale this form. Form overrides this to enforce a maximum / minimum size.
    /// </summary>
    protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
    {
        base.ScaleControl(factor, specified);

        Size minSize = MinimumSize;
        Size maxSize = MaximumSize;
        if (!MinimumSize.IsEmpty)
        {
            MinimumSize = ScaleSize(minSize, factor.Width, factor.Height);
        }

        if (!MaximumSize.IsEmpty)
        {
            MaximumSize = ScaleSize(maxSize, factor.Width, factor.Height);
        }
    }

    /// <summary>
    ///  This is called when the ToolStripDropDownItem sets the DropDown property using CreateDefaultDropDown.
    ///  In this case, the IsAutoGenerated should return true.
    /// </summary>
    internal void SetAutoGeneratedInternal(bool autoGenerated)
    {
        _isAutoGenerated = autoGenerated;
    }

    /// <summary>
    ///  Sync sizes with the ToolStripDropDown
    /// </summary>
    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        Rectangle bounds = new(x, y, width, height);
        bounds = GetDropDownBounds(bounds);

        base.SetBoundsCore(bounds.X, bounds.Y, bounds.Width, bounds.Height, specified);
    }

    private void SetTopLevelStyle(bool value)
    {
        if (!IsHandleCreated)
        {
            return;
        }

        // We need to swap they style bits on the window handle
        // we could recreate the handle, but that seems rather expensive.
        WINDOW_STYLE styleFlags = WindowStyle;

        if (value)
        {
            // Setting toplevel = true
            styleFlags &= ~WINDOW_STYLE.WS_CHILD;
            styleFlags |= WINDOW_STYLE.WS_POPUP;
        }
        else
        {
            // This is a child window
            styleFlags &= ~WINDOW_STYLE.WS_POPUP;
            styleFlags |= WINDOW_STYLE.WS_CHILD;
        }

        WindowStyle = styleFlags;
    }

    protected override void SetVisibleCore(bool visible)
    {
        if (_state[s_stateInSetVisibleCore])
        {
            return;
        }

        _state[s_stateInSetVisibleCore] = true;
        try
        {
            if (visible)
            {
                if (LayoutRequired)
                {
                    LayoutTransaction.DoLayout(this, this, PropertyNames.Visible);
                }

                // Assume that it's been cancelled so that if we throw we do nothing.
                bool openingEventCancelled = true;

                try
                {
                    // Add opening event.
                    // Snap the foreground window BEFORE calling any user events so they
                    // don't have a chance to activate something else. This covers the case
                    // where someone handles the opening event and throws up a messagebox.
                    HWND foregroundWindow = PInvokeCore.GetForegroundWindow();

                    // Fire Opening event
                    // Cancellable event in which default value of e.Cancel depends on
                    // the number of displayed items >0.
                    CancelEventArgs openEventArgs = new(cancel: DisplayedItems.Count == 0);
                    OnOpening(openEventArgs);

                    openingEventCancelled = openEventArgs.Cancel;

                    if (!openingEventCancelled)
                    {
                        // Do the actual work to open the window.
                        if (TopLevel)
                        {
                            ReparentToActiveToolStripWindow();
                        }

                        if (OwnerToolStrip is not null)
                        {
                            OwnerToolStrip.ActiveDropDowns.Add(this);

                            // the act of showing this window can cause a spurious mouse move
                            // in the parent, make sure it retains where the mouse really was.
                            OwnerToolStrip.SnapMouseLocation();

                            // Make sure that mouse capture transitions between the owner and dropdown.
                            if (OwnerToolStrip.Capture)
                            {
                                Capture = true;
                            }
                        }

                        base.SetVisibleCore(visible);

                        if (TopLevel)
                        {
                            ApplyTopMost(true);
                        }
                        else if (IsHandleCreated && 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 | SET_WINDOW_POS_FLAGS.SWP_NOACTIVATE);
                        }
                    }
                }
                finally
                {
                    // Fire the opened event only if we actually opened the control.
                    if (!openingEventCancelled)
                    {
                        OnOpened(EventArgs.Empty);
                    }
                }
            }
            else
            {
                if (Visible)
                {
                    ToolStripDropDownCloseReason reason = _closeReason;
                    ResetCloseReason();

                    ToolStripDropDownClosingEventArgs e = new(reason);

                    // Fire Closing Event
                    // Cancel is prepopulated based on AutoClose feature.
                    e.Cancel = e.CloseReason != ToolStripDropDownCloseReason.CloseCalled && !AutoClose;

                    try
                    {
                        OnClosing(e);
                    }
                    finally
                    {
                        // delay evaluate only in the case we need it
                        if (!e.Cancel)
                        {
                            // setting to not visible. Dismiss our child drop downs, reset, set ourselves visible false.
                            DismissActiveDropDowns();

                            // Make sure we cancel auto expansion on the root
                            ToolStrip? topLevelToolStrip = GetToplevelOwnerToolStrip();
                            ToolStrip? parentToolStrip = OwnerItem?.ParentInternal;

                            // We don't consider reason == ToolStripDropDownCloseReason.Keyboard here.
                            // DropDown needs to be closed when Alt or ESC is pressed,
                            // but these two keys are handled in ToolStrip.RestoreFocusInternal()
                            // and ToolStripDropDown.SelectPreviousToolStrip() respectively,
                            // and ToolStrip.MenuAutoExpand of top level tool strip will be set false there.
                            // Left and Right keys may also close dropdown, but we don't need to
                            // set ToolStrip.MenuAutoExpand of top level tool strip to be false in such cases.
                            if ((reason == ToolStripDropDownCloseReason.AppClicked) ||
                                (reason == ToolStripDropDownCloseReason.ItemClicked) ||
                                (reason == ToolStripDropDownCloseReason.CloseCalled && topLevelToolStrip == parentToolStrip) ||
                                (reason == ToolStripDropDownCloseReason.AppFocusChange && topLevelToolStrip == parentToolStrip))
                            {
                                CancelAutoExpand();
                            }

                            // if this came through via a click event we should actually
                            // dismiss everyone in the chain. Other windows will receive a
                            // close, closing event with reason AppFocusChange. This is by
                            // design since the item wasn't clicked on that window.
                            if (reason == ToolStripDropDownCloseReason.ItemClicked)
                            {
                                // Preserve the SourceControl value up the chain.
                                _saveSourceControl = true;
                                DismissAll();

                                // make sure that when we roll up, our owner item's selection is cleared.
                                ToolStripItem? rootOwnerItem = GetToplevelOwnerItem();
                                rootOwnerItem?.Unselect();

                                ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this);
                                ToolStripManager.ModalMenuFilter.ExitMenuMode();
                            }
                            else
                            {
                                ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this);
                            }

                            // snap our bounds, we'll need it for some invalidation later.
                            Rectangle bounds = Bounds;

                            try
                            {
                                base.SetVisibleCore(visible);
                            }
                            finally
                            {
                                // Remove ourselves from the active dropdown list.
                                OwnerToolStrip?.ActiveDropDowns.Remove(this);
                                ActiveDropDowns.Clear();

                                // If the user traps the click event and starts pumping their
                                // own messages by calling Application.DoEvents, we should
                                // release mouse capture.
                                if (Capture)
                                {
                                    Capture = false;
                                }
                            }

                            // Fire OnClosed.
                            // if you make VisibleChanged throw you don't get closed. Sorry.
                            ToolStripDropDownClosedEventArgs closedEventArgs = new(reason);
                            OnClosed(closedEventArgs);

                            if (TopLevel && (!IsDisposed || !Disposing))
                            {
                                // Parent back up to our DropDownOwnerWindow.
                                ReparentToDropDownOwnerWindow();
                            }

                            if (!_saveSourceControl)
                            {
                                Debug.Assert(reason != ToolStripDropDownCloseReason.ItemClicked,
                                    "Why are we resetting SourceControl on a click event?");

                                // If we're not about to fire a Click event, reset SourceControl.
                                SourceControlInternal = null;
                            }

                            // Making ourselves look presentable:
                            // We may be about to invoke a click event here...
                            // if we're the topmost dropdown then invalidate our
                            // intersection with the toplevel toolstrip
                            if (!DesignMode && IsFirstDropDown && OwnerItem is not null && TopLevel)
                            {
                                ToolStrip? toolStrip = GetToplevelOwnerToolStrip();
                                if (toolStrip is not null && !(toolStrip.IsDisposed || toolStrip.Disposing))
                                {
                                    // translate the bounds (already in screen coords) to toolstrip.
                                    bounds.Location = toolStrip.PointToClient(bounds.Location);

                                    // find the intersection with the client and use that to invalidate
                                    bounds.Intersect(toolStrip.ClientRectangle);

                                    if (bounds.Width > 0 && bounds.Height > 0)
                                    {
                                        toolStrip.Invalidate(bounds);
                                        toolStrip.Update();
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    ToolStripManager.ModalMenuFilter.RemoveActiveToolStrip(this);
                }
            }
        }
        finally
        {
            _state[s_stateInSetVisibleCore] = false;
            _saveSourceControl = false;
        }
    }

    private bool ShouldSerializeDefaultDropDownDirection()
    {
        return (_childDropDownDirection != ToolStripDropDownDirection.Default);
    }

    /// <summary>
    ///  Updates the layered window attributes if the control is in layered mode.
    /// </summary>
    private void UpdateLayered()
    {
        if (_state[s_stateLayered] && IsHandleCreated && TopLevel)
        {
            if (!PInvoke.SetLayeredWindowAttributes(this, (COLORREF)0, OpacityAsByte, LAYERED_WINDOW_ATTRIBUTES_FLAGS.LWA_ALPHA))
            {
                throw new Win32Exception();
            }
        }
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new void Show()
    {
        // don't set displayLocation here, since all the other Show methods call this.
        base.Show();
    }

    /// <summary> show relative to control coordinates </summary>
    public void Show(Control control, Point position)
    {
        SourceControlInternal = control.OrThrowIfNull();
        // When we have no owner item and we're set to RTL.Inherit, translate the coordinates
        // so that the menu looks like it's swooping from the other side
        if (OwnerItem is null && control.RightToLeft == RightToLeft.Yes)
        {
            AdjustSize();
            position.Offset(control.IsMirrored ? Width : -Width, 0);
        }

        _displayLocation = control.PointToScreen(position);
        Location = _displayLocation;
        ShowCore();
    }

    public void Show(Control control, Point position, ToolStripDropDownDirection direction)
    {
        SourceControlInternal = control.OrThrowIfNull();
        _displayLocation = CalculateDropDownLocation(control.PointToScreen(position), direction).Location;
        Location = _displayLocation;
        ShowCore();
    }

    /// <summary> show relative to control coordinates </summary>
    public void Show(Control control, int x, int y)
    {
        SourceControlInternal = control.OrThrowIfNull();
        Show(control, new Point(x, y));
    }

    /// <summary> show relative to screen coordinates </summary>
    public void Show(Point screenLocation)
    {
        _displayLocation = screenLocation;
        Location = _displayLocation;
        ShowCore();
    }

    public void Show(Point position, ToolStripDropDownDirection direction)
    {
        _displayLocation = CalculateDropDownLocation(position, direction).Location;
        Location = _displayLocation;
        ShowCore();
    }

    /// <summary> show relative to screen coordinates </summary>
    public void Show(int x, int y)
    {
        _displayLocation = new Point(x, y);
        Location = _displayLocation;
        ShowCore();
    }

    private void ShowCore()
    {
        Show();
    }

    private bool ShouldSerializeDropShadowEnabled()
    {
        return !_dropShadowEnabled;
    }

    internal override bool ShouldSerializeLayoutStyle()
    {
        return LayoutStyle != ToolStripLayoutStyle.VerticalStackWithOverflow;
    }

    internal void UnassignDropDownItem()
    {
        Debug.Assert(_countDropDownItemsAssignedTo > 0, "dropdown assignment underflow");
        _countDropDownItemsAssignedTo = Math.Max(--_countDropDownItemsAssignedTo, 0);
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.MsgInternal)
        {
            case PInvokeCore.WM_NCACTIVATE:
                // if someone clicks on a child control of the toolstrip dropdown, we want
                // the title bar to continue appearing active. Normally we just show without
                // taking window activation (ShowWindow(SHOWNOACTIVATE)) but we can't stop
                // child controls from taking focus.
                WmNCActivate(ref m);
                return;
            case PInvokeCore.WM_ACTIVATE:
                // This is the Chrome Panel collection editor scenario
                // we had focus, then the Chrome panel was activated and we never went away
                // when we get focus again, we should reactivate our message filter.

                if ((nint)m.WParamInternal == PInvoke.WA_ACTIVE)
                {
                    if (Visible)
                    {
                        if (ToolStripManager.ModalMenuFilter.GetActiveToolStrip() != this)
                        {
                            // if we were inactive and now we are, we should enter menu mode
                            ToolStripManager.ModalMenuFilter.SetActiveToolStrip(this);
                        }
                    }
                    else
                    {
                        Debug.Fail($"Why are we being activated when we're not visible? Deactivating thing is {WindowsFormsUtils.GetControlInformation((HWND)(nint)m.LParamInternal)}");
                    }
                }

                base.WndProc(ref m);
                return;
            default:
                base.WndProc(ref m);
                return;
        }
    }

    #region DropDownSpecific
    internal void DismissAll()
    {
        ToolStripDropDown toplevel = GetFirstDropDown();
        toplevel._closeReason = _closeReason;
        toplevel.DismissActiveDropDowns();
        toplevel._saveSourceControl = _saveSourceControl;
        toplevel.Visible = false;
    }

    private void DismissActiveDropDowns()
    {
        int count = ActiveDropDowns.Count;
        if (count == 1)
        {
            // this is the most common case
            ActiveDropDowns[0].Visible = false;
        }
        else
        {
            List<ToolStripDropDown> dropDowns = [..ActiveDropDowns];

            // We can't iterate through the active dropdown collection
            // here as changing visibility changes the collection.
            for (int i = 0; i < dropDowns.Count; i++)
            {
                dropDowns[i].Visible = false;
            }
        }
    }

    #region WMNCACTIVATE
    private bool _sendingActivateMessage;

    /// <summary>
    ///  If someone clicks on a child control of the toolstrip dropdown, we want
    ///  the title bar to continue appearing active. Normally we just show without
    ///  taking window activation (ShowWindow(SHOWNOACTIVATE)) but we can't stop
    ///  child controls from taking focus.
    /// </summary>
    private unsafe void WmNCActivate(ref Message m)
    {
        if (m.WParamInternal == 0u)
        {
            base.WndProc(ref m);
        }
        else
        {
            if (!_sendingActivateMessage)
            {
                _sendingActivateMessage = true;
                try
                {
                    // We're activating - notify the previous guy that we're activating.
                    HandleRef<HWND> activeWindow = ToolStripManager.ModalMenuFilter.ActiveHwnd;

                    PInvokeCore.SendMessage(activeWindow, PInvokeCore.WM_NCACTIVATE, (WPARAM)(BOOL)true, (LPARAM)(-1));
                    PInvoke.RedrawWindow(
                        activeWindow,
                        lprcUpdate: null,
                        HRGN.Null,
                        REDRAW_WINDOW_FLAGS.RDW_FRAME | REDRAW_WINDOW_FLAGS.RDW_INVALIDATE);

                    m.WParamInternal = 1u;
                }
                finally
                {
                    _sendingActivateMessage = false;
                }
            }

            DefWndProc(ref m);
            return;
        }
    }
    #endregion

    /// <summary>
    ///  Determines if this is the first dropDown in the dropDown chain
    /// </summary>
    internal bool IsFirstDropDown => OwnerToolStrip as ToolStripDropDown is null;

    /// <summary>
    ///  returns the root dropdown in the chain.
    /// </summary>
    internal ToolStripDropDown GetFirstDropDown()
    {
        ToolStripDropDown topmost = this;
        // walk back up the chain of windows to get the topmost
        ToolStripDropDown? ownerDropDown = topmost.OwnerToolStrip as ToolStripDropDown;
        while (ownerDropDown is not null)
        {
            topmost = ownerDropDown;
            ownerDropDown = topmost.OwnerToolStrip as ToolStripDropDown;
        }

        return topmost;
    }

    internal static ToolStripDropDown? GetFirstDropDown(ToolStrip start)
    {
        Debug.Assert(start is not null, "Who is passing null to GetFirstDropDown?");
        if ((start is null) || (!start.IsDropDown))
        {
            return null;
        }

        ToolStripDropDown startDropDown = (ToolStripDropDown)start;
        return startDropDown.GetFirstDropDown();
    }

    #endregion DropDownSpecific
}
