|
// 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 = new(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
}
|