|
//------------------------------------------------------------------------------
// <copyright file="ToolStripDropDownItem.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Diagnostics;
using System.Security.Permissions;
using System.Windows.Forms.Layout;
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem"]/*' />
/// <devdoc>
/// Base class for ToolStripItems that display DropDown windows.
/// </devdoc>
[Designer("System.Windows.Forms.Design.ToolStripMenuItemDesigner, " + AssemblyRef.SystemDesign)]
[DefaultProperty("DropDownItems")]
public abstract class ToolStripDropDownItem : ToolStripItem {
private ToolStripDropDown dropDown = null;
private ToolStripDropDownDirection toolStripDropDownDirection = ToolStripDropDownDirection.Default;
private static readonly object EventDropDownShow = new object();
private static readonly object EventDropDownHide = new object();
private static readonly object EventDropDownOpened = new object();
private static readonly object EventDropDownClosed = new object();
private static readonly object EventDropDownItemClicked = new object();
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.ToolStripDropDownItem"]/*' />
/// <devdoc>
/// Protected ctor so you can't create one of these without deriving from it.
/// </devdoc>
protected ToolStripDropDownItem() {
}
protected ToolStripDropDownItem(string text, Image image, EventHandler onClick) : base(text, image, onClick) {
}
protected ToolStripDropDownItem(string text, Image image, EventHandler onClick, string name) : base(text, image, onClick, name) {
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
protected ToolStripDropDownItem(string text, Image image, params ToolStripItem[] dropDownItems) : this(text, image, (EventHandler)null) {
if (dropDownItems != null) {
this.DropDownItems.AddRange(dropDownItems);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDown"]/*' />
/// <devdoc>
/// The ToolStripDropDown that will be displayed when this item is clicked.
/// </devdoc>
[
TypeConverter(typeof(ReferenceConverter)),
SRCategory(SR.CatData),
SRDescription(SR.ToolStripDropDownDescr)
]
public ToolStripDropDown DropDown {
get {
if (dropDown == null) {
DropDown = CreateDefaultDropDown();
if (!(this is ToolStripOverflowButton))
{
dropDown.SetAutoGeneratedInternal(true);
}
if (ParentInternal != null) {
dropDown.ShowItemToolTips = ParentInternal.ShowItemToolTips;
}
}
return dropDown;
}
set {
if (dropDown != value) {
if (dropDown != null) {
dropDown.Opened -= new EventHandler(DropDown_Opened);
dropDown.Closed -= new ToolStripDropDownClosedEventHandler(DropDown_Closed);
dropDown.ItemClicked -= new ToolStripItemClickedEventHandler(DropDown_ItemClicked);
dropDown.UnassignDropDownItem();
}
dropDown = value;
if (dropDown != null) {
dropDown.Opened += new EventHandler(DropDown_Opened);
dropDown.Closed += new ToolStripDropDownClosedEventHandler(DropDown_Closed);
dropDown.ItemClicked += new ToolStripItemClickedEventHandler(DropDown_ItemClicked);
dropDown.AssignToDropDownItem();
}
}
}
}
// the area which activates the dropdown.
internal virtual Rectangle DropDownButtonArea {
get { return this.Bounds; }
}
[Browsable(false)]
[SRDescription(SR.ToolStripDropDownItemDropDownDirectionDescr)]
[SRCategory(SR.CatBehavior)]
public ToolStripDropDownDirection DropDownDirection {
get {
if (toolStripDropDownDirection == ToolStripDropDownDirection.Default) {
ToolStrip parent = ParentInternal;
if (parent != null) {
ToolStripDropDownDirection dropDownDirection = parent.DefaultDropDownDirection;
if (OppositeDropDownAlign || this.RightToLeft != parent.RightToLeft && (this.RightToLeft != RightToLeft.Inherit)) {
dropDownDirection = RTLTranslateDropDownDirection(dropDownDirection, RightToLeft);
}
if (IsOnDropDown) {
// we gotta make sure that we dont collide with the existing menu.
Rectangle bounds = GetDropDownBounds(dropDownDirection);
Rectangle ownerItemBounds = new Rectangle(TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords), Size);
Rectangle intersectionBetweenChildAndParent = Rectangle.Intersect(bounds, ownerItemBounds);
// grab the intersection
if (intersectionBetweenChildAndParent.Width >= 2) {
RightToLeft toggledRightToLeft = (RightToLeft == RightToLeft.Yes) ? RightToLeft.No : RightToLeft.Yes;
ToolStripDropDownDirection newDropDownDirection = RTLTranslateDropDownDirection(dropDownDirection, toggledRightToLeft);
// verify that changing the dropdown direction actually causes less intersection.
int newIntersectionWidth = Rectangle.Intersect(GetDropDownBounds(newDropDownDirection), ownerItemBounds).Width;
if (newIntersectionWidth < intersectionBetweenChildAndParent.Width) {
dropDownDirection = newDropDownDirection;
}
}
}
return dropDownDirection;
}
}
// someone has set a custom override
return toolStripDropDownDirection;
}
set {
// cant use Enum.IsValid as its not sequential
switch (value) {
case ToolStripDropDownDirection.AboveLeft:
case ToolStripDropDownDirection.AboveRight:
case ToolStripDropDownDirection.BelowLeft:
case ToolStripDropDownDirection.BelowRight:
case ToolStripDropDownDirection.Left:
case ToolStripDropDownDirection.Right:
case ToolStripDropDownDirection.Default:
break;
default:
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolStripDropDownDirection));
}
if (toolStripDropDownDirection != value) {
toolStripDropDownDirection = value;
if (HasDropDownItems && DropDown.Visible) {
DropDown.Location = DropDownLocation;
}
}
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDownClosed"]/*' />
/// <devdoc>
/// Occurs when the dropdown is closed
/// </devdoc>
[
SRCategory(SR.CatAction),
SRDescription(SR.ToolStripDropDownClosedDecr)
]
public event EventHandler DropDownClosed {
add {
Events.AddHandler(EventDropDownClosed, value);
}
remove {
Events.RemoveHandler(EventDropDownClosed, value);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDownLocation"]/*' />
internal protected virtual Point DropDownLocation
{
get {
if (ParentInternal == null || !HasDropDownItems) {
return Point.Empty;
}
ToolStripDropDownDirection dropDownDirection = DropDownDirection;
return GetDropDownBounds(dropDownDirection).Location;
}
}
/// <include file='doc\WinBarPopupItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDownOpening"]/*' />
[
SRCategory(SR.CatAction),
SRDescription(SR.ToolStripDropDownOpeningDescr)
]
public event EventHandler DropDownOpening {
add {
Events.AddHandler(EventDropDownShow, value);
}
remove {
Events.RemoveHandler(EventDropDownShow, value);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDownOpened"]/*' />
/// <devdoc>
/// Occurs when the dropdown is opened
/// </devdoc>
[
SRCategory(SR.CatAction),
SRDescription(SR.ToolStripDropDownOpenedDescr)
]
public event EventHandler DropDownOpened {
add {
Events.AddHandler(EventDropDownOpened, value);
}
remove {
Events.RemoveHandler(EventDropDownOpened, value);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDownItems"]/*' />
/// <devdoc>
/// Returns the DropDown's items collection.
/// </devdoc>
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
SRCategory(SR.CatData),
SRDescription(SR.ToolStripDropDownItemsDescr)
]
public ToolStripItemCollection DropDownItems {
get {
return DropDown.Items;
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.DropDownItemClicked"]/*' />
/// <devdoc>
/// Occurs when the dropdown is opened
/// </devdoc>
[SRCategory(SR.CatAction)]
public event ToolStripItemClickedEventHandler DropDownItemClicked {
add {
Events.AddHandler(EventDropDownItemClicked, value);
}
remove {
Events.RemoveHandler(EventDropDownItemClicked, value);
}
}
/// <include file='doc\ToolStripPopupItem.uex' path='docs/doc[@for="ToolStripDropDownItem.HasDropDownItems"]/*' />
[Browsable(false)]
public virtual bool HasDropDownItems {
get {
//VSWhidbey 354665: use count of visible DisplayedItems instead so that we take into account things that arent visible
return (dropDown != null) && dropDown.HasVisibleItems;
}
}
/// <include file='doc\ToolStripPopupItem.uex' path='docs/doc[@for="ToolStripDropDownItem.HasDropDown"]/*' />
[Browsable(false)]
public bool HasDropDown {
get { return dropDown != null; }
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.Pressed"]/*' />
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override bool Pressed {
get {
//
if (dropDown != null) {
if (DropDown.AutoClose || !IsInDesignMode || (IsInDesignMode && !IsOnDropDown)) {
return DropDown.OwnerItem == this && DropDown.Visible;
}
}
return base.Pressed;
}
}
internal virtual bool OppositeDropDownAlign {
get { return false; }
}
internal virtual void AutoHide(ToolStripItem otherItemBeingSelected) {
HideDropDown();
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.CreateAccessibilityInstance"]/*' />
protected override AccessibleObject CreateAccessibilityInstance() {
return new ToolStripDropDownItemAccessibleObject(this);
}
/// <include file='doc\ToolStripPopupItem.uex' path='docs/doc[@for="ToolStripDropDownItem.CreateDefaultDropDown"]/*' />
protected virtual ToolStripDropDown CreateDefaultDropDown() {
// AutoGenerate a Winbar DropDown - set the property so we hook events
return new ToolStripDropDown(this, true);
}
private Rectangle DropDownDirectionToDropDownBounds(ToolStripDropDownDirection dropDownDirection, Rectangle dropDownBounds) {
Point offset = Point.Empty;
switch (dropDownDirection) {
case ToolStripDropDownDirection.AboveLeft:
offset.X = -dropDownBounds.Width + this.Width;
offset.Y = -dropDownBounds.Height + 1;
break;
case ToolStripDropDownDirection.AboveRight:
offset.Y = -dropDownBounds.Height + 1;
break;
case ToolStripDropDownDirection.BelowRight:
offset.Y = this.Height - 1;
break;
case ToolStripDropDownDirection.BelowLeft:
offset.X = -dropDownBounds.Width + this.Width;
offset.Y = this.Height - 1;
break;
case ToolStripDropDownDirection.Right:
offset.X = this.Width;
if (!IsOnDropDown) {
// overlap the toplevel toolstrip
offset.X -= 1;
}
break;
case ToolStripDropDownDirection.Left:
offset.X = -dropDownBounds.Width;
break;
}
Point itemScreenLocation = this.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords);
dropDownBounds.Location = new Point(itemScreenLocation.X + offset.X, itemScreenLocation.Y + offset.Y);
dropDownBounds = WindowsFormsUtils.ConstrainToScreenWorkingAreaBounds(dropDownBounds);
return dropDownBounds;
}
private void DropDown_Closed(object sender, ToolStripDropDownClosedEventArgs e) {
OnDropDownClosed(EventArgs.Empty);
}
private void DropDown_Opened(object sender, EventArgs e) {
OnDropDownOpened(EventArgs.Empty);
}
private void DropDown_ItemClicked(object sender, ToolStripItemClickedEventArgs e) {
OnDropDownItemClicked(e);
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.Dispose"]/*' />
/// <devdoc>
/// Make sure we unhook dropdown events.
/// </devdoc>
protected override void Dispose(bool disposing)
{
if (this.dropDown != null) {
dropDown.Opened -= new EventHandler(DropDown_Opened);
dropDown.Closed -= new ToolStripDropDownClosedEventHandler(DropDown_Closed);
dropDown.ItemClicked -= new ToolStripItemClickedEventHandler(DropDown_ItemClicked);
if (disposing && dropDown.IsAutoGenerated) {
// if we created the dropdown, dispose it and its children.
dropDown.Dispose();
dropDown = null;
}
}
base.Dispose(disposing);
}
private Rectangle GetDropDownBounds(ToolStripDropDownDirection dropDownDirection) {
Rectangle dropDownBounds = new Rectangle(Point.Empty, DropDown.GetSuggestedSize());
// calculate the offset from the upper left hand corner of the item.
dropDownBounds = DropDownDirectionToDropDownBounds(dropDownDirection, dropDownBounds);
// we should make sure we dont obscure the owner item.
Rectangle itemScreenBounds = new Rectangle(this.TranslatePoint(Point.Empty, ToolStripPointType.ToolStripItemCoords, ToolStripPointType.ScreenCoords), this.Size);
if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Height > 1) {
bool rtl = (RightToLeft == RightToLeft.Yes);
// try positioning to the left
if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Width > 1) {
dropDownBounds = DropDownDirectionToDropDownBounds(!rtl ? ToolStripDropDownDirection.Right : ToolStripDropDownDirection.Left, dropDownBounds);
}
// try positioning to the right
if (Rectangle.Intersect(dropDownBounds, itemScreenBounds).Width > 1) {
dropDownBounds = DropDownDirectionToDropDownBounds(!rtl ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right, dropDownBounds);
}
}
return dropDownBounds;
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.HideDropDown"]/*' />
/// <devdoc>
/// Hides the DropDown, if it is visible.
/// </devdoc>
public void HideDropDown() {
// consider - CloseEventArgs to prevent shutting down.
OnDropDownHide(EventArgs.Empty);
if (this.dropDown != null && this.dropDown.Visible) {
DropDown.Visible = false;
if (AccessibilityImprovements.Level1) {
AccessibilityNotifyClients(AccessibleEvents.StateChange);
AccessibilityNotifyClients(AccessibleEvents.NameChange);
}
}
}
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
if (dropDown != null) {
dropDown.OnOwnerItemFontChanged(EventArgs.Empty);
}
}
/// <include file='doc\ToolStripItem.uex' path='docs/doc[@for="ToolStripItem.OnBoundsChanged"]/*' />
protected override void OnBoundsChanged() {
base.OnBoundsChanged();
//Reset the Bounds...
if (this.dropDown != null && this.dropDown.Visible)
{
this.dropDown.Bounds = GetDropDownBounds(DropDownDirection);
}
}
protected override void OnRightToLeftChanged(EventArgs e) {
base.OnRightToLeftChanged(e);
if (HasDropDownItems) {
// only perform a layout on a visible dropdown - otherwise clear the preferred size cache.
if (DropDown.Visible) {
LayoutTransaction.DoLayout(DropDown, this, PropertyNames.RightToLeft);
}
else {
CommonProperties.xClearPreferredSizeCache(DropDown);
DropDown.LayoutRequired = true;
}
}
}
internal override void OnImageScalingSizeChanged(EventArgs e) {
base.OnImageScalingSizeChanged(e);
if (HasDropDown && DropDown.IsAutoGenerated) {
DropDown.DoLayoutIfHandleCreated(new ToolStripItemEventArgs(this));
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.OnDropDownHide"]/*' />
/// <devdoc>
/// Called as a response to HideDropDown
/// </devdoc>
protected virtual void OnDropDownHide(EventArgs e) {
this.Invalidate();
EventHandler handler = (EventHandler)Events[EventDropDownHide];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.OnDropDownShow"]/*' />
/// <devdoc>
/// Last chance to stick in the DropDown before it is shown.
/// </devdoc>
protected virtual void OnDropDownShow(EventArgs e) {
EventHandler handler = (EventHandler)Events[EventDropDownShow];
if (handler != null) {
handler(this, e);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.OnDropDownOpened"]/*' />
/// <devdoc>
/// called when the default item is clicked
/// </devdoc>
protected internal virtual void OnDropDownOpened(System.EventArgs e) {
// only send the event if we're the thing that currently owns the DropDown.
if (DropDown.OwnerItem == this) {
EventHandler handler = (EventHandler)Events[EventDropDownOpened];
if (handler != null) handler(this, e);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.OnDropDownClosed"]/*' />
/// <devdoc>
/// called when the default item is clicked
/// </devdoc>
protected internal virtual void OnDropDownClosed(System.EventArgs e) {
// only send the event if we're the thing that currently owns the DropDown.
this.Invalidate();
if (DropDown.OwnerItem == this) {
EventHandler handler = (EventHandler)Events[EventDropDownClosed];
if (handler != null) handler(this, e);
if (!DropDown.IsAutoGenerated) {
DropDown.OwnerItem = null;
}
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.OnDropDownItemClicked"]/*' />
/// <devdoc>
/// called when the default item is clicked
/// </devdoc>
protected internal virtual void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) {
// only send the event if we're the thing that currently owns the DropDown.
if (DropDown.OwnerItem == this) {
ToolStripItemClickedEventHandler handler = (ToolStripItemClickedEventHandler)Events[EventDropDownItemClicked];
if (handler != null) handler(this, e);
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected internal override bool ProcessCmdKey(ref Message m, Keys keyData) {
if (HasDropDownItems) {
return DropDown.ProcessCmdKeyInternal(ref m, keyData);
}
return base.ProcessCmdKey(ref m, keyData);
}
/// <include file='doc\ToolStripPopupItem.uex' path='docs/doc[@for="ToolStripDropDownItem.ProcessDialogKey"]/*' />
[UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
protected internal override bool ProcessDialogKey(Keys keyData) {
Keys keyCode = (Keys)keyData & Keys.KeyCode;
if (HasDropDownItems) {
// VSWhidbey 478068: items on the overflow should have the same kind of keyboard handling as a toplevel
bool isToplevel = (!IsOnDropDown || IsOnOverflow);
if (isToplevel && (keyCode == Keys.Down || keyCode == Keys.Up || keyCode == Keys.Enter || (SupportsSpaceKey && keyCode == Keys.Space))) {
Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] open submenu from toplevel item");
if (Enabled || DesignMode) {
// |__[ * File ]_____| * is where you are. Up or down arrow hit should expand menu
this.ShowDropDown();
if (!AccessibilityImprovements.UseLegacyToolTipDisplay) {
KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this);
}
this.DropDown.SelectNextToolStripItem(null, true);
}// else eat the key
return true;
}
else if (!isToplevel) {
// if we're on a DropDown - then cascade out.
bool menusCascadeRight = (((int)DropDownDirection & 0x0001) == 0);
bool forward = ((keyCode == Keys.Enter) || (SupportsSpaceKey && keyCode == Keys.Space));
forward = (forward || (menusCascadeRight && keyCode == Keys.Left) || (!menusCascadeRight && keyCode == Keys.Right));
if (forward) {
Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] open submenu from NON-toplevel item");
if (Enabled || DesignMode) {
this.ShowDropDown();
if (!AccessibilityImprovements.UseLegacyToolTipDisplay) {
KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this);
}
this.DropDown.SelectNextToolStripItem(null, true);
} // else eat the key
return true;
}
}
}
if (IsOnDropDown) {
bool menusCascadeRight = (((int)DropDownDirection & 0x0001) == 0);
bool backward = ((menusCascadeRight && keyCode == Keys.Right) || (!menusCascadeRight && keyCode == Keys.Left));
if (backward) {
Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] close submenu from NON-toplevel item");
// we're on a drop down but we're heading back up the chain.
// remember to select the item that displayed this dropdown.
ToolStripDropDown parent = GetCurrentParentDropDown();
if (parent != null && !parent.IsFirstDropDown) {
// we're walking back up the dropdown chain.
parent.SetCloseReason(ToolStripDropDownCloseReason.Keyboard);
if (!AccessibilityImprovements.UseLegacyToolTipDisplay) {
KeyboardToolTipStateMachine.Instance.NotifyAboutLostFocus(this);
}
parent.SelectPreviousToolStrip();
return true;
}
// else if (parent.IsFirstDropDown)
// the base handling (ToolStripDropDown.ProcessArrowKey) will perform auto-expansion of
// the previous item in the menu.
}
}
Debug.WriteLineIf(ToolStrip.SelectionDebug.TraceVerbose, "[SelectDBG ProcessDialogKey] ddi calling base");
return base.ProcessDialogKey(keyData);
}
private ToolStripDropDownDirection RTLTranslateDropDownDirection(ToolStripDropDownDirection dropDownDirection, RightToLeft rightToLeft) {
switch (dropDownDirection) {
case ToolStripDropDownDirection.AboveLeft:
return ToolStripDropDownDirection.AboveRight;
case ToolStripDropDownDirection.AboveRight:
return ToolStripDropDownDirection.AboveLeft;
case ToolStripDropDownDirection.BelowRight:
return ToolStripDropDownDirection.BelowLeft;
case ToolStripDropDownDirection.BelowLeft:
return ToolStripDropDownDirection.BelowRight;
case ToolStripDropDownDirection.Right:
return ToolStripDropDownDirection.Left;
case ToolStripDropDownDirection.Left:
return ToolStripDropDownDirection.Right;
}
Debug.Fail("Why are we here");
// dont expect it to come to this but just in case here are the real defaults.
if (IsOnDropDown) {
return (rightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.Left : ToolStripDropDownDirection.Right;
}
else {
return (rightToLeft == RightToLeft.Yes) ? ToolStripDropDownDirection.BelowLeft : ToolStripDropDownDirection.BelowRight;
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItem.ShowDropDown"]/*' />
/// <devdoc>
/// Shows the DropDown, if one is set.
/// </devdoc>
public void ShowDropDown() {
this.ShowDropDown(false);
}
internal void ShowDropDown(bool mousePush) {
this.ShowDropDownInternal();
ToolStripDropDownMenu menu = this.dropDown as ToolStripDropDownMenu;
if (menu != null) {
if (!mousePush) {
menu.ResetScrollPosition();
}
menu.RestoreScrollPosition();
}
}
private void ShowDropDownInternal() {
if (this.dropDown == null || (!this.dropDown.Visible)) {
// VSWhidbey 469145 we want to show if there's no dropdown
// or if the dropdown is not visible.
OnDropDownShow(EventArgs.Empty);
}
// the act of setting the drop down visible the first time sets the parent
// it seems that GetVisibleCore returns true if your parent is null.
if (this.dropDown != null && !this.dropDown.Visible) {
if (this.dropDown.IsAutoGenerated && this.DropDownItems.Count <= 0) {
return; // this is a no-op for autogenerated drop downs.
}
if (this.DropDown == this.ParentInternal) {
throw new InvalidOperationException(SR.GetString(SR.ToolStripShowDropDownInvalidOperation));
}
this.dropDown.OwnerItem = this;
this.dropDown.Location = DropDownLocation;
this.dropDown.Show();
this.Invalidate();
if (AccessibilityImprovements.Level1) {
AccessibilityNotifyClients(AccessibleEvents.StateChange);
AccessibilityNotifyClients(AccessibleEvents.NameChange);
}
}
}
private bool ShouldSerializeDropDown() {
return dropDown != null && !dropDown.IsAutoGenerated;
}
private bool ShouldSerializeDropDownDirection() {
return (toolStripDropDownDirection != ToolStripDropDownDirection.Default);
}
private bool ShouldSerializeDropDownItems() {
return (dropDown != null && dropDown.IsAutoGenerated);
}
internal override void OnKeyboardToolTipHook(ToolTip toolTip) {
base.OnKeyboardToolTipHook(toolTip);
KeyboardToolTipStateMachine.Instance.Hook(this.DropDown, toolTip);
}
internal override void OnKeyboardToolTipUnhook(ToolTip toolTip) {
base.OnKeyboardToolTipUnhook(toolTip);
KeyboardToolTipStateMachine.Instance.Unhook(this.DropDown, toolTip);
}
// This is de facto behind the 4.8/EnableDpiMessaging/ToolStrip HighDpi quirks.
internal override void ToolStrip_RescaleConstants(int oldDpi, int newDpi) {
RescaleConstantsInternal(newDpi);
// Traversing the tree of DropDownMenuItems non-recursively to set new
// Font (where necessary because not inherited from parent), DeviceDpi and reset the scaling.
var itemsStack = new System.Collections.Generic.Stack<ToolStripDropDownItem>();
itemsStack.Push(this);
while(itemsStack.Count > 0) {
var item = itemsStack.Pop();
if (item.dropDown != null) {
// The following does not get set, since dropDown has no parent/is not part of the
// controls collection, so this gets never called through the normal inheritance chain.
item.dropDown.deviceDpi = newDpi;
item.dropDown.ResetScaling(newDpi);
foreach (ToolStripItem childItem in item.DropDown.Items) {
if (childItem == null) continue;
// Checking if font was inherited from parent.
Font local = childItem.Font;
if (!local.Equals(childItem.OwnerItem?.Font)) {
var factor = (float)newDpi / oldDpi;
childItem.Font = new Font(local.FontFamily, local.Size * factor, local.Style,
local.Unit, local.GdiCharSet, local.GdiVerticalFont);
}
childItem.DeviceDpi = newDpi;
if (typeof(ToolStripDropDownItem).IsAssignableFrom(childItem.GetType())) {
if (((ToolStripDropDownItem)childItem).dropDown != null) {
itemsStack.Push((ToolStripDropDownItem)childItem);
}
}
}
}
}
// It's important to call the base class method only AFTER we processed all DropDown items,
// because we need the new DPI in place, before a Font change triggers new layout calc.
base.ToolStrip_RescaleConstants(oldDpi, newDpi);
}
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItemAccessibleObject"]/*' />
[System.Runtime.InteropServices.ComVisible(true)]
public class ToolStripDropDownItemAccessibleObject : ToolStripItem.ToolStripItemAccessibleObject {
private ToolStripDropDownItem owner;
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItemAccessibleObject.ToolStripDropDownItemAccessibleObject"]/*' />
public ToolStripDropDownItemAccessibleObject(ToolStripDropDownItem item) : base(item) {
owner = item;
}
/// <include file='doc\ToolStripDropDownItem.uex' path='docs/doc[@for="ToolStripDropDownItemAccessibleObject.Role"]/*' />
public override AccessibleRole Role {
get {
AccessibleRole role = Owner.AccessibleRole;
if (role != AccessibleRole.Default) {
return role;
}
return AccessibleRole.MenuItem;
}
}
/// <include file='doc\ToolStripItem.uex' path='docs/doc[@for="ToolStripItemAccessibleObject.DoDefaultAction"]/*' />
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public override void DoDefaultAction() {
ToolStripDropDownItem item = Owner as ToolStripDropDownItem;
if (item != null && item.HasDropDownItems) {
item.ShowDropDown();
}
else {
base.DoDefaultAction();
}
}
internal override bool IsIAccessibleExSupported() {
if (AccessibilityImprovements.Level3 && owner.Parent != null && (owner.Parent.IsInDesignMode || owner.Parent.IsTopInDesignMode)) {
return false;
}
if (owner!= null && AccessibilityImprovements.Level1 ) {
return true;
}
else {
return base.IsIAccessibleExSupported();
}
}
internal override bool IsPatternSupported(int patternId) {
if (patternId == NativeMethods.UIA_ExpandCollapsePatternId && owner.HasDropDownItems) {
return true;
}
else {
return base.IsPatternSupported(patternId);
}
}
internal override object GetPropertyValue(int propertyID) {
if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_IsOffscreenPropertyId && owner != null && owner.Owner is ToolStripDropDown) {
return !((ToolStripDropDown)owner.Owner).Visible;
}
return base.GetPropertyValue(propertyID);
}
internal override void Expand() {
DoDefaultAction();
}
internal override void Collapse() {
if (owner != null && owner.DropDown != null && owner.DropDown.Visible) {
owner.DropDown.Close();
}
}
internal override UnsafeNativeMethods.ExpandCollapseState ExpandCollapseState {
get {
return owner.DropDown.Visible ? UnsafeNativeMethods.ExpandCollapseState.Expanded : UnsafeNativeMethods.ExpandCollapseState.Collapsed;
}
}
public override AccessibleObject GetChild(int index) {
if ((owner == null) || !owner.HasDropDownItems) {
return null;
}
return owner.DropDown.AccessibilityObject.GetChild(index);
}
public override int GetChildCount() {
if ((owner == null) || !owner.HasDropDownItems) {
return -1;
}
// Do not expose child items when the submenu is collapsed to prevent Narrator from announcing
// invisible menu items when Narrator is in item's mode (CAPSLOCK + Arrow Left/Right) or
// in scan mode (CAPSLOCK + Space)
if (AccessibilityImprovements.Level3 && ExpandCollapseState == UnsafeNativeMethods.ExpandCollapseState.Collapsed) {
return 0;
}
if (owner.DropDown.LayoutRequired) {
LayoutTransaction.DoLayout(owner.DropDown, owner.DropDown, PropertyNames.Items);
}
return owner.DropDown.AccessibilityObject.GetChildCount();
}
internal int GetChildFragmentIndex(ToolStripItem.ToolStripItemAccessibleObject child) {
if ((owner == null) || (owner.DropDownItems == null)) {
return -1;
}
for (int i = 0; i < owner.DropDownItems.Count; i++) {
if (owner.DropDownItems[i].Available && child.Owner == owner.DropDownItems[i]) {
return i;
}
}
return -1;
}
/// <summary>
/// Gets the number of children belonging to an accessible object.
/// </summary>
/// <returns>The number of children.</returns>
internal int GetChildFragmentCount() {
if ((owner == null) || (owner.DropDownItems == null)) {
return -1;
}
int count = 0;
for (int i = 0; i < owner.DropDownItems.Count; i++) {
if (owner.DropDownItems[i].Available) {
count++;
}
}
return count;
}
internal AccessibleObject GetChildFragment(int index) {
var toolStripAccessibleObject = owner.DropDown.AccessibilityObject as ToolStrip.ToolStripAccessibleObject;
if (toolStripAccessibleObject != null) {
return toolStripAccessibleObject.GetChildFragment(index);
}
return null;
}
internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
if (owner == null || owner.DropDown == null) {
return null;
}
switch (direction) {
case UnsafeNativeMethods.NavigateDirection.FirstChild:
int childCount = GetChildCount();
if (childCount > 0) {
return GetChildFragment(0);
}
return null;
case UnsafeNativeMethods.NavigateDirection.LastChild:
childCount = GetChildCount();
if (childCount > 0) {
return GetChildFragment(childCount - 1);
}
return null;
case UnsafeNativeMethods.NavigateDirection.NextSibling:
case UnsafeNativeMethods.NavigateDirection.PreviousSibling:
ToolStripDropDown dropDown = owner.Owner as ToolStripDropDown;
if (dropDown == null) {
break;
}
int index = dropDown.Items.IndexOf(owner);
if (index == -1) {
Debug.Fail("No item matched the index?");
return null;
}
index += direction == UnsafeNativeMethods.NavigateDirection.NextSibling ? 1 : -1;
if (index >= 0 && index < dropDown.Items.Count) {
var item = dropDown.Items[index];
var controlHostItem = item as ToolStripControlHost;
if (controlHostItem != null) {
return controlHostItem.ControlAccessibilityObject;
}
return item.AccessibilityObject;
}
return null;
}
return base.FragmentNavigate(direction);
}
}
}
|