Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Controls / MenuItem.cs / 1 / MenuItem.cs
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Utility;
using System.Diagnostics;
using System.Windows.Threading;
using System.Globalization;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Security;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Shapes;
using System.Windows.Markup;
// Disable CS3001: Warning as Error: not CLS-compliant
#pragma warning disable 3001
namespace System.Windows.Controls
{
///
/// Defines the different placement types of MenuItems.
///
public enum MenuItemRole
{
///
/// A top-level menu item that can invoke commands.
///
TopLevelItem,
///
/// Header for top-level menus.
///
TopLevelHeader,
///
/// A menu item in a submenu that can invoke commands.
///
SubmenuItem,
///
/// A header for a submenu.
///
SubmenuHeader,
}
///
/// A child item of Menu.
/// MenuItems can be selected to invoke commands.
/// MenuItems can be headers for submenus.
/// MenuItems can be checked or unchecked.
///
[DefaultEvent("Click")]
[Localizability(LocalizationCategory.Menu)]
[TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(MenuItem))]
public class MenuItem : HeaderedItemsControl, ICommandSource
{
// ---------------------------------------------------------------------------
// Defines the names of the resources to be consumed by the MenuItem style.
// Used to restyle several roles of MenuItem without having to restyle
// all of the control.
// ---------------------------------------------------------------------------
#region StyleKeys
///
/// Key used to mark the template for use by TopLevel MenuItems
///
public static ResourceKey TopLevelItemTemplateKey
{
get
{
if (_topLevelItemTemplateKey == null)
{
_topLevelItemTemplateKey = new ComponentResourceKey(typeof(MenuItem), "TopLevelItemTemplateKey");
}
return _topLevelItemTemplateKey;
}
}
///
/// Key used to mark the template for use by TopLevel Menu Header
///
public static ResourceKey TopLevelHeaderTemplateKey
{
get
{
if (_topLevelHeaderTemplateKey == null)
{
_topLevelHeaderTemplateKey = new ComponentResourceKey(typeof(MenuItem), "TopLevelHeaderTemplateKey");
}
return _topLevelHeaderTemplateKey;
}
}
///
/// Key used to mark the template for use by Submenu Item
///
public static ResourceKey SubmenuItemTemplateKey
{
get
{
if (_submenuItemTemplateKey == null)
{
_submenuItemTemplateKey = new ComponentResourceKey(typeof(MenuItem), "SubmenuItemTemplateKey");
}
return _submenuItemTemplateKey;
}
}
///
/// Key used to mark the template for use by Submenu Header
///
public static ResourceKey SubmenuHeaderTemplateKey
{
get
{
if (_submenuHeaderTemplateKey == null)
{
_submenuHeaderTemplateKey = new ComponentResourceKey(typeof(MenuItem), "SubmenuHeaderTemplateKey");
}
return _submenuHeaderTemplateKey;
}
}
private static ComponentResourceKey _topLevelItemTemplateKey;
private static ComponentResourceKey _topLevelHeaderTemplateKey;
private static ComponentResourceKey _submenuItemTemplateKey;
private static ComponentResourceKey _submenuHeaderTemplateKey;
#endregion
//--------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
///
/// Default MenuItem constructor
///
public MenuItem() : base()
{
}
static MenuItem()
{
HeaderProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceHeader)));
EventManager.RegisterClassHandler(typeof(MenuItem), AccessKeyManager.AccessKeyPressedEvent, new AccessKeyPressedEventHandler(OnAccessKeyPressed));
EventManager.RegisterClassHandler(typeof(MenuItem), MenuBase.IsSelectedChangedEvent, new RoutedPropertyChangedEventHandler(OnIsSelectedChanged));
ForegroundProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemColors.MenuTextBrush));
FontFamilyProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily));
FontSizeProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize));
FontStyleProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontStyle));
FontWeightProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontWeight));
// Disable tooltips on menu item when submenu is open
ToolTipService.IsEnabledProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceToolTipIsEnabled)));
#if OLD_AUTOMATION
AutomationProvider.AcceleratorKeyProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(null, (PropertyChangedCallback)null, new CoerceValueCallback(OnCoerceAcceleratorKey)));
#endif
DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(typeof(MenuItem)));
_dType = DependencyObjectType.FromSystemTypeInternal(typeof(MenuItem));
KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(KeyboardNavigationMode.None));
// Disable default focus visual for MenuItem.
FocusVisualStyleProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata((object)null /* default value */));
// While the menu is opened, Input Method should be suspended.
// the docusmen focus of Cicero should not be changed but key typing should not be
// dispatched to IME/TIP.
InputMethod.IsInputMethodSuspendedProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
}
#endregion
//--------------------------------------------------------------------
//
// Public Events
//
//--------------------------------------------------------------------
#region Public Events
///
/// Event corresponds to left mouse button click
///
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Add / Remove Click handler
///
[Category("Behavior")]
public event RoutedEventHandler Click
{
add
{
AddHandler(MenuItem.ClickEvent, value);
}
remove
{
RemoveHandler(MenuItem.ClickEvent, value);
}
}
///
/// Event that is fired when mouse button is pressed down but before menus are closed.
/// This event should be handled by the parent menu and used to know when to close all submenus.
///
internal static readonly RoutedEvent PreviewClickEvent = EventManager.RegisterRoutedEvent("PreviewClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Checked event
///
public static readonly RoutedEvent CheckedEvent = EventManager.RegisterRoutedEvent("Checked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Unchecked event
///
public static readonly RoutedEvent UncheckedEvent = EventManager.RegisterRoutedEvent("Unchecked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Add / Remove Checked handler
///
[Category("Behavior")]
public event RoutedEventHandler Checked
{
add
{
AddHandler(CheckedEvent, value);
}
remove
{
RemoveHandler(CheckedEvent, value);
}
}
///
/// Add / Remove Unchecked handler
///
[Category("Behavior")]
public event RoutedEventHandler Unchecked
{
add
{
AddHandler(UncheckedEvent, value);
}
remove
{
RemoveHandler(UncheckedEvent, value);
}
}
///
/// Event fires when submenu opens
///
public static readonly RoutedEvent SubmenuOpenedEvent =
EventManager.RegisterRoutedEvent("SubmenuOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Event fires when submenu closes
///
public static readonly RoutedEvent SubmenuClosedEvent =
EventManager.RegisterRoutedEvent("SubmenuClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Add / Remove SubmenuOpenedEvent handler
///
[Category("Behavior")]
public event RoutedEventHandler SubmenuOpened
{
add
{
AddHandler(SubmenuOpenedEvent, value);
}
remove
{
RemoveHandler(SubmenuOpenedEvent, value);
}
}
///
/// Add / Remove SubmenuClosedEvent handler
///
[Category("Behavior")]
public event RoutedEventHandler SubmenuClosed
{
add
{
AddHandler(SubmenuClosedEvent, value);
}
remove
{
RemoveHandler(SubmenuClosedEvent, value);
}
}
#endregion
//-------------------------------------------------------------------
//
// Public Properties
//
//--------------------------------------------------------------------
#region Public Properties
// Set the header to the command text if no header has been explicitly specified
private static object CoerceHeader(DependencyObject d, object value)
{
MenuItem menuItem = (MenuItem)d;
RoutedUICommand uiCommand;
// If no header has been set, use the command's text
if (value == null && !menuItem.HasNonDefaultValue(HeaderProperty))
{
uiCommand = menuItem.Command as RoutedUICommand;
if (uiCommand != null)
{
value = uiCommand.Text;
}
return value;
}
// If the header had been set to a UICommand by the ItemsControl, replace it with the command's text
uiCommand = value as RoutedUICommand;
if (uiCommand != null)
{
// The header is equal to the command.
// If this MenuItem was generated for the command, then go ahead and overwrite the header
// since the generator automatically set the header.
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(menuItem);
if (parent != null)
{
object originalItem = parent.ItemContainerGenerator.ItemFromContainer(menuItem);
if (originalItem == value)
{
return uiCommand.Text;
}
}
}
return value;
}
///
/// The DependencyProperty for the RoutedCommand.
/// Flags: None
/// Default Value: null
///
public static readonly DependencyProperty CommandProperty =
ButtonBase.CommandProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata(
(ICommand)null,
new PropertyChangedCallback(OnCommandChanged)));
///
/// The MenuItem's Command.
///
[Bindable(true), Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem item = (MenuItem) d;
item.OnCommandChanged((ICommand) e.OldValue, (ICommand) e.NewValue);
}
private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
{
if (oldCommand != null)
{
UnhookCommand(oldCommand);
}
if (newCommand != null)
{
HookCommand(newCommand);
}
CoerceValue(HeaderProperty);
CoerceValue(InputGestureTextProperty);
}
private void UnhookCommand(ICommand command)
{
EventHandler handler = CanExecuteChangedHandler.GetValue(this);
if (handler != null)
{
command.CanExecuteChanged -= handler;
CanExecuteChangedHandler.ClearValue(this);
}
UpdateCanExecute();
}
private void HookCommand(ICommand command)
{
EventHandler handler = new EventHandler(OnCanExecuteChanged);
CanExecuteChangedHandler.SetValue(this, handler);
command.CanExecuteChanged += handler;
UpdateCanExecute();
}
private void OnCanExecuteChanged(object sender, EventArgs e)
{
UpdateCanExecute();
}
private void UpdateCanExecute()
{
SetBoolField(BoolField.CanExecuteInvalid, false);
if (Command != null)
{
// Perf optimization - only raise CanExecute event if the menu is open
MenuItem parent = ItemsControl.ItemsControlFromItemContainer(this) as MenuItem;
if (parent == null || parent.IsSubmenuOpen)
{
CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this);
}
else
{
CanExecute = true;
SetBoolField(BoolField.CanExecuteInvalid, true);
}
}
else
{
CanExecute = true;
}
}
///
/// Fetches the value of the IsEnabled property
///
///
/// The reason this property is overridden is so that MenuItem
/// can infuse the value for CanExecute into it.
///
protected override bool IsEnabledCore
{
get
{
return base.IsEnabledCore && CanExecute;
}
}
///
/// The DependencyProperty for the RoutedCommand's parameter.
/// Flags: None
/// Default Value: null
///
public static readonly DependencyProperty CommandParameterProperty =
ButtonBase.CommandParameterProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata((object) null));
///
/// The parameter to pass to MenuItem's Command.
///
[Bindable(true), Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
///
/// The DependencyProperty for Target property
/// Flags: None
/// Default Value: null
///
public static readonly DependencyProperty CommandTargetProperty =
ButtonBase.CommandTargetProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata((IInputElement) null));
///
/// The target element on which to fire the command.
///
[Bindable(true), Category("Action")]
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
///
/// The DependencyProperty for the IsSubmenuOpen property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsSubmenuOpenProperty =
DependencyProperty.Register(
"IsSubmenuOpen",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnIsSubmenuOpenChanged),
new CoerceValueCallback(CoerceIsSubmenuOpen)));
///
/// When the MenuItem's submenu is visible.
///
[Bindable(true), Browsable(false), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsSubmenuOpen
{
get { return (bool) GetValue(IsSubmenuOpenProperty); }
set { SetValue(IsSubmenuOpenProperty, BooleanBoxes.Box(value)); }
}
private static object CoerceIsSubmenuOpen(DependencyObject d, object value)
{
if ((bool) value)
{
MenuItem mi = (MenuItem) d;
if (!mi.IsLoaded)
{
mi.RegisterToOpenOnLoad();
return BooleanBoxes.FalseBox;
}
}
return value;
}
// Disable tooltips on opened menu items
private static object CoerceToolTipIsEnabled(DependencyObject d, object value)
{
MenuItem mi = (MenuItem) d;
return mi.IsSubmenuOpen ? BooleanBoxes.FalseBox : value;
}
private void RegisterToOpenOnLoad()
{
Loaded += new RoutedEventHandler(OpenOnLoad);
}
private void OpenOnLoad(object sender, RoutedEventArgs e)
{
// Open menu after it has rendered (Loaded is fired before 1st render)
Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
{
CoerceValue(IsSubmenuOpenProperty);
return null;
}), null);
}
///
/// Called when IsSubmenuOpenID is invalidated on "d."
///
private static void OnIsSubmenuOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem menuItem = (MenuItem)d;
bool oldValue = (bool) e.OldValue;
bool newValue = (bool) e.NewValue;
// The IsSubmenuOpen value has changed; this should stop any timers
// we may have set to open/close the menus.
menuItem.StopTimer(ref menuItem._openHierarchyTimer);
menuItem.StopTimer(ref menuItem._closeHierarchyTimer);
MenuItemAutomationPeer peer = UIElementAutomationPeer.FromElement(menuItem) as MenuItemAutomationPeer;
if (peer != null)
{
peer.ResetChildrenCache();
peer.RaiseExpandCollapseAutomationEvent(oldValue, newValue);
}
if (newValue)
{
CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on commands
// When menuitem's submenu opens, it should be selected.
menuItem.IsSelected = true;
MenuItemRole role = menuItem.Role;
if (role == MenuItemRole.TopLevelHeader)
{
menuItem.SetMenuMode(true);
}
menuItem.CurrentSelection = null;
// When our submenu opens, update our siblings so they do not animate
menuItem.NotifySiblingsToSuspendAnimation();
// Force update of CanExecute when opening menu.
for (int i = 0; i < menuItem.Items.Count; i++)
{
MenuItem subItem = menuItem.ItemContainerGenerator.ContainerFromIndex(i) as MenuItem;
if (subItem != null && subItem.GetBoolField(BoolField.CanExecuteInvalid))
{
subItem.UpdateCanExecute();
}
}
menuItem.OnSubmenuOpened(new RoutedEventArgs(SubmenuOpenedEvent, menuItem));
menuItem.SetBoolField(BoolField.IgnoreMouseEvents, true);
menuItem.SetBoolField(BoolField.MouseEnterOnMouseMove, false);
// MenuItem should ignore any mouse enter or move events until the menu has fully
// opened. Otherwise we may highlight a menu item under the mouse even though
// the user opened the menu with the keyboard
// This is fired below input priority so any mouse events happen before setting the flag
menuItem.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate(object param)
{
menuItem.SetBoolField(BoolField.IgnoreMouseEvents, false);
return null;
}), null);
}
else
{
// Our submenu is closing, so close our submenu's submenu
if (menuItem.CurrentSelection != null)
{
// We're about to close the submenu -- if focus is within
// the subtree, we need to take it back so that Focus isn't
// left in an orphaned tree.
if (menuItem.CurrentSelection.IsKeyboardFocusWithin)
{
menuItem.Focus();
}
if (menuItem.CurrentSelection.IsSubmenuOpen)
{
menuItem.CurrentSelection.IsSubmenuOpen = false;
}
}
else
{
// We need to take focus out of the subtree if we close
// the submenu. Above we can be sure that focus will be
// on the selected item so we just need to check if IsFocusWithin
// is true on the selected item. If we have no CurrentSelection,
// we have to be a little more aggressive and take focus
// back if IsFocusWithin is true.
//
// NOTE: This could potentially steal focus back from something
// within the menuitem's header (say, a TextBox) but it is
// unlikely that focus will be within a header while the submenu
// is open.
if (menuItem.IsKeyboardFocusWithin)
{
if (!menuItem.Focus())
{
// Shoot, we couldn't take focus out of the submenu
// and put it back on ourselves. Now focus is in a
// disconnected subtree. Ultimately core input will
// disallow this, presumably by setting focus to null.
// For now we won't handle this case.
}
}
}
menuItem.CurrentSelection = null;
if ((menuItem.IsMouseOver) && (menuItem.Role == MenuItemRole.SubmenuHeader))
{
// If the mouse is inside the subtree, then we will get a mouse leave, but we want to ignore it
// to maintain the highlight.
menuItem.SetBoolField(BoolField.IgnoreNextMouseLeave, true);
}
// When our submenu closes, update our children so they will animate
menuItem.NotifyChildrenToResumeAnimation();
// No Popup in the style so fire closed now
if (menuItem._submenuPopup == null)
{
menuItem.OnSubmenuClosed(new RoutedEventArgs(SubmenuClosedEvent, menuItem));
}
}
menuItem.CoerceValue(ToolTipService.IsEnabledProperty);
}
private void OnPopupClosed(object source, EventArgs e)
{
OnSubmenuClosed(new RoutedEventArgs(SubmenuClosedEvent, this));
}
///
///
///
///
protected virtual void OnSubmenuOpened(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
///
///
///
protected virtual void OnSubmenuClosed(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// The key needed set a read-only property.
///
private static readonly DependencyPropertyKey RolePropertyKey =
DependencyProperty.RegisterReadOnly(
"Role",
typeof(MenuItemRole),
typeof(MenuItem),
new FrameworkPropertyMetadata(MenuItemRole.TopLevelItem));
///
/// The DependencyProperty for the Role property.
/// Flags: None
/// Default Value: MenuItemRole.TopLevelItem
///
public static readonly DependencyProperty RoleProperty =
RolePropertyKey.DependencyProperty;
///
/// What the role of the menu item is: TopLevelItem, TopLevelHeader, SubmenuItem, SubmenuHeader.
///
[Category("Behavior")]
public MenuItemRole Role
{
get { return (MenuItemRole) GetValue(RoleProperty); }
}
private void UpdateRole()
{
MenuItemRole type;
if (!IsCheckable && HasItems)
{
if (LogicalParent is Menu)
{
type = MenuItemRole.TopLevelHeader;
}
else
{
type = MenuItemRole.SubmenuHeader;
}
}
else
{
if (LogicalParent is Menu)
{
type = MenuItemRole.TopLevelItem;
}
else
{
type = MenuItemRole.SubmenuItem;
}
}
SetValue(RolePropertyKey, type);
}
///
/// The DependencyProperty for the IsCheckable property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsCheckableProperty =
DependencyProperty.Register(
"IsCheckable",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
new PropertyChangedCallback(OnIsCheckableChanged)));
///
/// IsCheckable determines the user ability to check/uncheck the item.
///
[Bindable(true), Category("Behavior")]
public bool IsCheckable
{
get { return (bool)GetValue(IsCheckableProperty); }
set { SetValue(IsCheckableProperty, value); }
}
private static void OnIsCheckableChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
((MenuItem) target).UpdateRole();
}
///
/// The DependencyPropertyKey for the IsPressed property.
/// Flags: None
/// Default Value: false
///
private static readonly DependencyPropertyKey IsPressedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsPressed",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// The DependencyProperty for the IsPressed property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty;
///
/// When the MenuItem is pressed.
///
[Browsable(false), Category("Appearance")]
public bool IsPressed
{
get { return (bool) GetValue(IsPressedProperty); }
protected set { SetValue(IsPressedPropertyKey, BooleanBoxes.Box(value)); }
}
private void UpdateIsPressed()
{
Rect itemBounds = new Rect(new Point(), RenderSize);
if ((Mouse.LeftButton == MouseButtonState.Pressed) &&
IsMouseOver &&
itemBounds.Contains(Mouse.GetPosition(this)))
{
IsPressed = true;
}
else
{
ClearValue(IsPressedPropertyKey);
}
}
///
/// The key needed set a read-only property.
///
private static readonly DependencyPropertyKey IsHighlightedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsHighlighted",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// The DependencyProperty for the IsHighlighted property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsHighlightedProperty =
IsHighlightedPropertyKey.DependencyProperty;
///
/// Whether the MenuItem should be highlighted.
///
[Browsable(false), Category("Appearance")]
public bool IsHighlighted
{
get { return (bool) GetValue(IsHighlightedProperty); }
protected set { SetValue(IsHighlightedPropertyKey, BooleanBoxes.Box(value)); }
}
///
/// The DependencyProperty for the IsChecked property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register(
"IsChecked",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnIsCheckedChanged)));
///
/// When the MenuItem is checked.
///
[Bindable(true), Category("Appearance")]
public bool IsChecked
{
get { return (bool) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, BooleanBoxes.Box(value)); }
}
///
/// Called when IsChecked becomes true.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
protected virtual void OnChecked(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// Called when IsChecked becomes false.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
protected virtual void OnUnchecked(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// Called when IsCheckedProperty is invalidated on "d."
///
private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem menuItem = (MenuItem) d;
if ((bool) e.NewValue)
{
menuItem.OnChecked(new RoutedEventArgs(CheckedEvent));
}
else
{
menuItem.OnUnchecked(new RoutedEventArgs(UncheckedEvent));
}
}
///
/// The DependencyProperty for the StaysOpenOnClick property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty StaysOpenOnClickProperty =
DependencyProperty.Register(
"StaysOpenOnClick",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// Indicates that the submenu that this MenuItem is within should not close when this item is clicked.
///
[Bindable(true), Category("Behavior")]
public bool StaysOpenOnClick
{
get { return (bool) GetValue(StaysOpenOnClickProperty); }
set { SetValue(StaysOpenOnClickProperty, BooleanBoxes.Box(value)); }
}
///
/// True if this MenuItem is the current MenuItem of its parent.
/// Focus drives Selection, but not vice versa. This will enable
/// focusless menus.
///
internal bool IsSelected
{
get { return (bool) GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, BooleanBoxes.Box(value)); }
}
///
/// DependencyProperty for IsSelected property.
///
internal static readonly DependencyProperty IsSelectedProperty =
Selector.IsSelectedProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnIsSelectedChanged)));
private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem menuItem = (MenuItem)d;
// When IsSelected changes, IsHighlighted should reflect IsSelected
// Note: it is okay for IsHighlighted and IsSelected to be different.
// Selection and highlight will separate when mousing around in
// a submenu when any timers are active. Until you hover long
// enough and your selection is "committed", selection and highlight
// can disagree.
menuItem.SetValue(IsHighlightedPropertyKey, e.NewValue);
// If IsSelected is changing to false, make sure to close
// our submenu before doing anything.
if ((bool) e.OldValue)
{
if (menuItem.IsSubmenuOpen)
{
menuItem.IsSubmenuOpen = false;
}
// Also stop any timers immediately when we become deselected.
menuItem.StopTimer(ref menuItem._openHierarchyTimer);
menuItem.StopTimer(ref menuItem._closeHierarchyTimer);
}
menuItem.RaiseEvent(new RoutedPropertyChangedEventArgs((bool) e.OldValue, (bool) e.NewValue, MenuBase.IsSelectedChangedEvent));
}
///
/// Called when IsSelected changed on this element or any descendant.
///
private static void OnIsSelectedChanged(object sender, RoutedPropertyChangedEventArgs e)
{
// If IsSelected changed on a child of the MenuItem, change CurrentSelection
// to the element that sent the event and handle the event.
if (sender != e.OriginalSource)
{
MenuItem menuItem = (MenuItem)sender;
MenuItem source = e.OriginalSource as MenuItem;
if (source != null)
{
if (e.NewValue)
{
// If the item is now selected, we should stop any timers which will
// close the submenu. This is for the case where one mouses out of
// the current selection but then comes back.
if (menuItem.CurrentSelection == source)
{
menuItem.StopTimer(ref menuItem._closeHierarchyTimer);
}
// If the MenuItem is selected and it's a new item that's a child of ours,
// change the CurrentSelection.
if (menuItem.CurrentSelection != source && source.LogicalParent == menuItem)
{
if (menuItem.CurrentSelection != null && menuItem.CurrentSelection.IsSubmenuOpen)
{
menuItem.CurrentSelection.IsSubmenuOpen = false;
}
menuItem.CurrentSelection = source;
}
}
else
{
// If the item is no longer selected
// If the MenuItem has been deselected and it's the CurrentSelection,
// set our CurrentSelection to null.
if (menuItem.CurrentSelection == source)
{
menuItem.CurrentSelection = null;
}
}
// Mark the event as handled as long as it came from a MenuItem underneath us
// even if we didn't necessarily do anything.
e.Handled = true;
}
}
}
///
/// The DependencyProperty for the InputGestureText property.
/// Default Value: String.Empty
///
public static readonly DependencyProperty InputGestureTextProperty =
DependencyProperty.Register(
"InputGestureText",
typeof(string),
typeof(MenuItem),
new FrameworkPropertyMetadata(String.Empty,
new PropertyChangedCallback(OnInputGestureTextChanged),
new CoerceValueCallback(CoerceInputGestureText)));
///
/// Text describing an input gesture that will invoke the command tied to this item.
///
[Bindable(true), CustomCategory("Content")]
public string InputGestureText
{
get { return (string) GetValue(InputGestureTextProperty); }
set { SetValue(InputGestureTextProperty, value); }
}
private static void OnInputGestureTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
#if OLD_AUTOMATION
d.CoerceValue(AutomationProvider.AcceleratorKeyProperty);
#endif
}
// Gets the input gesture text from the command text if it hasn't been explicitly specified
private static object CoerceInputGestureText(DependencyObject d, object value)
{
MenuItem menuItem = (MenuItem)d;
RoutedCommand routedCommand;
if (String.IsNullOrEmpty((string)value) && !menuItem.HasNonDefaultValue(InputGestureTextProperty)
&& (routedCommand = menuItem.Command as RoutedCommand) != null )
{
InputGestureCollection col = routedCommand.InputGestures;
if ((col != null) && (col.Count >= 1))
{
// Search for the first key gesture
for (int i = 0; i < col.Count; i++)
{
KeyGesture keyGesture = ((IList)col)[i] as KeyGesture;
if (keyGesture != null)
{
return keyGesture.GetDisplayStringForCulture(CultureInfo.CurrentCulture);
}
}
}
}
return value;
}
///
/// The DependencyProperty for the Icon property.
/// Default Value: null
///
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(
"Icon",
typeof(object),
typeof(MenuItem),
new FrameworkPropertyMetadata((object)null));
///
/// Text describing an input gesture that will invoke the command tied to this item.
///
[Bindable(true), CustomCategory("Content")]
public object Icon
{
get { return GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
// This is used to disable animations after the menu has displayed once
private static readonly DependencyPropertyKey IsSuspendingPopupAnimationPropertyKey
= DependencyProperty.RegisterReadOnly("IsSuspendingPopupAnimation", typeof(bool), typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// Returns true if the Menu should suspend animations on its popup
///
public static readonly DependencyProperty IsSuspendingPopupAnimationProperty = IsSuspendingPopupAnimationPropertyKey.DependencyProperty;
///
/// Returns true if the Menu should suspend animations on its popup
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsSuspendingPopupAnimation
{
get
{
return (bool)GetValue(IsSuspendingPopupAnimationProperty);
}
internal set
{
SetValue(IsSuspendingPopupAnimationPropertyKey, BooleanBoxes.Box(value));
}
}
// When opening the menu item, tell all other menu items at the same
// level that their submenus should not animate
private void NotifySiblingsToSuspendAnimation()
{
// Don't need to set this property if it is already false
if (!IsSuspendingPopupAnimation)
{
bool openedWithKeyboard = GetBoolField(BoolField.OpenedWithKeyboard);
// When opened by the keyboard, don't animate - set menumode on all items
// otherwise ignore this MenuItem so it animates when opening
MenuItem ignore = openedWithKeyboard ? null : this;
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(this);
MenuBase.SetSuspendingPopupAnimation(parent, ignore, true);
if (!openedWithKeyboard)
{
// Delay setting InMenuMode on this until after bindings have done their
// work and opened the popup (if it exists)
Dispatcher.BeginInvoke(DispatcherPriority.Input,
(DispatcherOperationCallback)delegate(object arg)
{
((MenuItem)arg).IsSuspendingPopupAnimation = true;
return null;
},
this);
}
else
{
SetBoolField(BoolField.OpenedWithKeyboard, false);
}
}
}
// Set IsSuspendingAnimation=false on all our children
private void NotifyChildrenToResumeAnimation()
{
MenuBase.SetSuspendingPopupAnimation(this, null, false);
}
#endregion
//-------------------------------------------------------------------
//
// Protected Methods
//
//-------------------------------------------------------------------
#region Protected Methods
///
/// Creates AutomationPeer ( )
///
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
return new System.Windows.Automation.Peers.MenuItemAutomationPeer(this);
}
///
/// This virtual method in called when IsInitialized is set to true and it raises an Initialized event
///
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
UpdateRole();
#if OLD_AUTOMATION
CoerceValue(AutomationProvider.AcceleratorKeyProperty);
#endif
}
///
/// Prepare the element to display the item. This may involve
/// applying styles, setting bindings, etc.
///
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
MenuItem.PrepareMenuItem(element, item);
}
///
/// Automatically set the Command property if the data item that this MenuItem represents is a command.
///
internal static void PrepareMenuItem(DependencyObject element, object item)
{
MenuItem menuItem = element as MenuItem;
if (menuItem != null)
{
ICommand command = item as ICommand;
if (command != null)
{
if (!menuItem.HasNonDefaultValue(CommandProperty))
{
menuItem.Command = command;
}
}
if (menuItem.GetBoolField(BoolField.CanExecuteInvalid))
{
menuItem.UpdateCanExecute();
}
}
else
{
Separator separator = item as Separator;
if (separator != null)
{
bool hasModifiers;
BaseValueSourceInternal vs = separator.GetValueSource(StyleProperty, null, out hasModifiers);
if (vs <= BaseValueSourceInternal.ImplicitReference)
separator.SetResourceReference(StyleProperty, SeparatorStyleKey);
separator.DefaultStyleKey = SeparatorStyleKey;
}
}
}
///
/// This virtual method in called when the MenuItem is clicked and it raises a Click event
///
///
/// Critical - Calls OnClickImple which sets the userInitiated bit on a command, which is used
/// for security purposes later.
/// TreatAsSafe - passes false for userInitiated
///
[SecurityCritical, SecurityTreatAsSafe]
protected virtual void OnClick()
{
OnClickImpl(false);
}
///
/// Critical - accepts a parameter which may be used to set the userInitiated
/// bit on a command, which is used for security purposes later.
///
[SecurityCritical]
internal virtual void OnClickCore(bool userInitiated)
{
OnClick();
}
///
/// Critical - Calls InvokeClickAfterRender which sets the userInitiated
/// bit on a command, which is used for security purposes later.
///
[SecurityCritical]
internal void OnClickImpl(bool userInitiated)
{
if (IsCheckable)
{
IsChecked = !IsChecked;
}
// Sub menu items will always be focused if they are moused over or keyboard navigated onto.
// When you click on a top-level menu item it should take focus.
// Sub menu items will not be focused if the mouse has moved out of
// the active hierarchy and has not settled on a new hierarchy yet.
if (!IsKeyboardFocusWithin)
{
FocusOrSelect();
}
// Raise the preview click. This will be handled by the parent menu and cause this submenu to disappear.
// It will also block until render-priority queue items have completed.
RaiseEvent(new RoutedEventArgs(MenuItem.PreviewClickEvent, this));
// Raise the automation event first *before* raising the Click event -
// otherwise automation may not get the event until after raising the click
// event returns, which could be problematic if the handler for that event
// displayed a modal dialog or did other significant work.
if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this);
if (peer != null)
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
}
// We have just caused all the popup windows to be hidden and queued for async
// destroy (at < render priority). Hiding the window will cause the underlying windows
// to be queued for repaint -- we need to wait for any windows in our context to repaint.
Dispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(InvokeClickAfterRender), userInitiated);
}
///
/// Critical - sets the userInitiated bit on a command, which is used
/// for security purposes later.
///
[SecurityCritical]
private object InvokeClickAfterRender(object arg)
{
bool userInitiated = (bool)arg;
RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent, this));
MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(this, userInitiated);
return null;
}
///
/// Called when the left mouse button is pressed.
///
///
///
/// Critical: Sets an internal variable in case input was user initiated and button was pressed
/// TreatAsSafe: The variable is not exposed and there are link demands to block public callers
///
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
[SecurityCritical,SecurityTreatAsSafe]
#pragma warning disable 0688
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseDown(e);
UpdateIsPressed();
if (e.UserInitiated)
{
_userInitiatedPress = true;
}
}
base.OnMouseLeftButtonDown(e);
}
///
/// Called when the right mouse button is pressed.
///
///
///
/// Critical: Sets an internal variable in case input was user initiated and button was pressed
/// TreatAsSafe: The variable is not exposed and there are link demands to block public callers
///
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
[SecurityCritical,SecurityTreatAsSafe]
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseDown(e);
if (e.UserInitiated)
{
_userInitiatedPress = true;
}
}
base.OnMouseRightButtonDown(e);
}
///
/// Called when the left mouse button is released.
///
///
///
/// Critical - This code can be used to cause paste to occur programmatically.
///
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.InheritanceDemand , Unrestricted=true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseUp(e);
UpdateIsPressed();
_userInitiatedPress = false;
}
base.OnMouseLeftButtonUp(e);
}
///
/// Called when the right mouse button is released.
///
///
///
/// Critical - This code can be used to cause paste to occur programmatically.
///
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.InheritanceDemand , Unrestricted=true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseUp(e);
_userInitiatedPress = false;
}
base.OnMouseRightButtonUp(e);
}
#pragma warning restore 0688
private void HandleMouseDown(MouseButtonEventArgs e)
{
// ((0, 0), RenderSize) is the closest we can get to checking if the
// mouse event was on the header portion of the MenuItem (i.e. not on
// any part of the submenu)
Rect r = new Rect(new Point(), RenderSize);
if (r.Contains(e.GetPosition(this)))
{
if (e.ChangedButton == MouseButton.Left || (e.ChangedButton == MouseButton.Right && InsideContextMenu))
{
// Click happens on down for headers
MenuItemRole role = Role;
if (role == MenuItemRole.TopLevelHeader || role == MenuItemRole.SubmenuHeader)
{
ClickHeader();
}
}
}
// Handle mouse messages b/c they were over me, I just didn't use it
e.Handled = true;
}
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
private void HandleMouseUp(MouseButtonEventArgs e)
{
// See comment above in HandleMouseDown.
Rect r = new Rect(new Point(), RenderSize);
if (r.Contains(e.GetPosition(this)))
{
if (e.ChangedButton == MouseButton.Left || (e.ChangedButton == MouseButton.Right && InsideContextMenu))
{
// Click happens on up for items
MenuItemRole role = Role;
if (role == MenuItemRole.TopLevelItem || role == MenuItemRole.SubmenuItem)
{
if (_userInitiatedPress == true)
{
ClickItem(e.UserInitiated);
}
else
{
// This is the case where the mouse down happend on a different element
// but the moust up is happening on the menuitem. this is to prevent spoofing
// attacks where someone substitutes an element with a menu item
ClickItem(false);
}
}
//
/*
// Click happens on up for top level items that are already open
if (role == MenuItemRole.TopLevelHeader && IsSubmenuOpen)
{
ClickHeader();
e.Handled = true;
}
*/
}
}
if (e.ChangedButton != MouseButton.Right || InsideContextMenu)
{
// Handle all clicks unless there's a possibility of a ContextMenu inside a Menu.
e.Handled = true;
}
}
private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
bool isScope = false;
if (e.Target == null)
{
// MenuItem access key should not work if something else beside MenuBase has capture
if (Mouse.Captured == null || Mouse.Captured is MenuBase)
{
e.Target = menuItem;
// special case is if we are the original source and our submenu is open,
// this is the case where the mouse moved over the header and focus is on
// the menu item but really you want to access key processing to be in your
// submenu.
// This assumes that no one will ever directly register a MenuItem with the AKM.
if (e.OriginalSource == menuItem && menuItem.IsSubmenuOpen)
{
isScope = true;
}
}
else
{
e.Handled = true;
}
}
else if (e.Scope == null)
{
// We want menu items to be a scope, but not for any AKs in its header.
// If e.Target is already filled in, check if it's a MenuItem.
// If it is and it's not us, we are its scope (i.e. we're the first MenuItem
// above it in the chain). If it's not a MenuItem, we have to take the long way.
if (e.Target != menuItem && e.Target is MenuItem)
{
isScope = true;
}
else
{
// This case handles when you have some non-MenuItem in a menu that can be
// the target of access keys, like a Button.
// MenuItems are a scope for all access keys which are outside of themselves.
// e.Source is the logical element in which the event was raised.
// If we can walk from the source to ourselves, then we are not correct
// scope of this access key; some parent should be.
DependencyObject source = e.Source as DependencyObject;
while (source != null)
{
// If we walk up to this Menuitem, we are not the scope.
if (source == menuItem)
{
break;
}
UIElement uiElement = source as UIElement;
// If we walk up to an item which is one of our children, we are their scope.
if ((uiElement != null) && (ItemsControlFromItemContainer(uiElement) == menuItem))
{
isScope = true;
break;
}
source = GetFrameworkParent(source);
}
}
}
if (isScope)
{
e.Scope = menuItem;
e.Handled = true;
}
}
///
/// An event reporting the mouse entered or left this element.
///
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
MenuItemRole role = Role;
// When we're a top-level menuitem we have to check if the menu has capture.
// If it doesn't we fall to the else below where we are just mousing around
// the top-level menuitems.
// (Note that Submenu items/headers do not have to look for capture.)
if (((role == MenuItemRole.TopLevelHeader || role == MenuItemRole.TopLevelItem) && IsInMenuMode)
|| (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.SubmenuItem))
{
MouseLeaveInMenuMode(role);
}
else
{
// Here we don't have capture and we're just mousing over
// top-level menu items. IsSelected should correspond to IsMouseOver.
if (IsMouseOver != IsSelected)
{
IsSelected = IsMouseOver;
}
}
UpdateIsPressed();
}
///
/// This is the method that responds to the MouseEvent event.
///
protected override void OnMouseMove(MouseEventArgs e)
{
// Ignore any mouse moves on ourselves while the popup is opening.
MenuItem parent = ItemsControl.ItemsControlFromItemContainer(this) as MenuItem;
if (parent != null &&
parent.GetBoolField(BoolField.MouseEnterOnMouseMove))
{
parent.SetBoolField(BoolField.MouseEnterOnMouseMove, false);
MouseEnterHelper();
}
}
///
/// An event reporting the mouse entered or left this element.
///
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
MouseEnterHelper();
}
private void MouseEnterHelper()
{
MenuItem parent = ItemsControl.ItemsControlFromItemContainer(this) as MenuItem;
// Do not enter and highlight this item until the popup has opened
// This prevents immediately selecting a submenu item when opening the menu
// because the mouse was already where the menu item appeared
if (parent == null || !parent.GetBoolField(BoolField.IgnoreMouseEvents))
{
MenuItemRole role = Role;
// When we're a top-level menuitem we have to check if the menu has capture.
// If it doesn't we fall to the else below where we are just mousing around
// the top-level menuitems.
// (Note that Submenu items/headers do not have to look for capture.)
if (((role == MenuItemRole.TopLevelHeader || role == MenuItemRole.TopLevelItem) && OpenOnMouseEnter)
|| (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.SubmenuItem))
{
MouseEnterInMenuMode(role);
}
else
{
// Here we don't have capture and we're just mousing over
// top-level menu items. IsSelected should correspond to IsMouseOver.
if (IsMouseOver != IsSelected)
{
IsSelected = IsMouseOver;
}
}
UpdateIsPressed();
}
else
{
parent.SetBoolField(BoolField.MouseEnterOnMouseMove, true);
}
}
private void MouseEnterInMenuMode(MenuItemRole role)
{
switch (role)
{
case MenuItemRole.TopLevelHeader:
case MenuItemRole.TopLevelItem:
{
// When mousing over a top-level hierarchy, it should open immediately.
if (!IsSubmenuOpen)
{
OpenHierarchy(role);
}
}
break;
case MenuItemRole.SubmenuHeader:
case MenuItemRole.SubmenuItem:
{
// If the current sibling has an open hierarchy, we cannot
// move focus/selection immediately. Instead we must set
// a timer to open after MenuShowDelay ms. If the sibling has
// no hierarchy open, it is safe to select the item immediately.
MenuItem sibling = CurrentSibling;
if (sibling == null || !sibling.IsSubmenuOpen)
{
if (!IsSubmenuOpen)
{
// Try to focus/select this item.
FocusOrSelect();
}
else
{
// If the submenu is open, then it should already be selected.
Debug.Assert(IsSelected, "When IsSubmenuOpen = true, IsSelected should be true as well");
// Need to make sure that when we leave the hierarchy and come back
// that the item is highlighted.
IsHighlighted = true;
}
}
else
{
// Highlight this item and remove the highlight
// from its sibling selected MenuItem
sibling.IsHighlighted = false;
IsHighlighted = true;
}
// If the submenu isn't open already, OpenHierarchy after MenuShowDelay ms
if (!IsSelected || !IsSubmenuOpen)
{
// When the timout happens, OpenHierarchy will select this item
SetTimerToOpenHierarchy();
}
}
break;
}
// Now that we're over this menu hierarchy with the mouse, we
// should stop any timers which might cause this hierarchy to close.
StopTimer(ref _closeHierarchyTimer);
}
private void MouseLeaveInMenuMode(MenuItemRole role)
{
// When mouse moves out of a submenu item, we should deselect
// the item. This is what Win32 does, and our menus don't
// feel right without it.
if (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.SubmenuItem)
{
if (GetBoolField(BoolField.IgnoreNextMouseLeave))
{
// The mouse was within a submenu that closed. A submenu header is receiving this
// message, but we want to ignore this one.
SetBoolField(BoolField.IgnoreNextMouseLeave, false);
}
else
{
if (!IsSubmenuOpen)
{
// When the submenu isn't open we can deselect the item right away.
if (IsSelected)
{
IsSelected = false;
}
else
{
// If it's not selected it might just be highlighted,
// so remove the highlight.
IsHighlighted = false;
}
if (IsKeyboardFocusWithin)
{
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(this);
if (parent != null)
{
parent.Focus();
}
}
}
else
{
// If the submenu is open and the mouse moved to some sibling
// hierarchy, we need to delay and deselect the item after
// MenuShowDelay ms, as long as the item doesn't get re-selected.
if (IsMouseOverSibling)
{
SetTimerToCloseHierarchy();
}
}
}
}
// No matter what, we've left the menu item and we should
// stop any timer which would cause the item to open.
StopTimer(ref _openHierarchyTimer);
}
///
/// An event announcing that the keyboard is focused on this element.
///
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
// Focus drives selection. If a MenuItem is focused, it should
// select itself.
if (!e.Handled && e.NewFocus == this)
{
IsSelected = true;
}
}
///
/// Called when the focus is no longer on or within this element.
///
protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsKeyboardFocusWithinChanged(e);
if (IsKeyboardFocusWithin && !IsSelected)
{
// If an item within us got focus (probably programatically), we need to become selected
IsSelected = true;
}
}
///
/// If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true.
/// Then ScrollViewer will not handle keyboard input and leave it up to the control.
///
protected internal override bool HandlesScrolling
{
get { return true; }
}
///
/// This is the method that responds to the KeyDown event.
///
/// Event arguments
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
#pragma warning disable 0688
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
bool handled = false;
Key key = e.Key;
MenuItemRole role = Role;
FlowDirection flowDirection = FlowDirection;
// In Right to Left mode we switch Right and Left keys
if (flowDirection == FlowDirection.RightToLeft)
{
if (key == Key.Right)
{
key = Key.Left;
}
else if (key == Key.Left)
{
key = Key.Right;
}
}
switch (key)
{
case Key.Tab:
if (role == MenuItemRole.SubmenuHeader && IsSubmenuOpen && CurrentSelection == null)
{
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
NavigateToEnd(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
}
else
{
NavigateToStart(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
}
handled = true;
}
break;
case Key.Right:
if ((role == MenuItemRole.SubmenuHeader) && !IsSubmenuOpen)
{
OpenSubmenuWithKeyboard();
handled = true;
}
break;
case Key.Enter:
{
if (((role == MenuItemRole.SubmenuItem) || (role == MenuItemRole.TopLevelItem)))
{
Debug.Assert(IsHighlighted, "MenuItem got Key.Enter but was not highlighted -- focus did not follow highlight?");
ClickItem(e.UserInitiated);
handled = true;
}
else if (role == MenuItemRole.TopLevelHeader)
{
//
OpenSubmenuWithKeyboard();
handled = true;
}
else if (role == MenuItemRole.SubmenuHeader && !IsSubmenuOpen)
{
OpenSubmenuWithKeyboard();
handled = true;
}
}
break;
// If a menuitem gets a down or up key and the submenu is open, we should focus the first or last
// item in the submenu (respectively). If the submenu is not opened, this will be handled by Menu.
case Key.Down:
{
if (role == MenuItemRole.SubmenuHeader && IsSubmenuOpen && CurrentSelection == null)
{
NavigateToStart(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
handled = true;
}
}
break;
case Key.Up:
{
if (role == MenuItemRole.SubmenuHeader && IsSubmenuOpen && CurrentSelection == null)
{
NavigateToEnd(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
handled = true;
}
}
break;
case Key.Left:
case Key.Escape:
{
// If Left or Escape is pressed on a Submenu Item or Header, the submenu should be closed.
// Closing the submenu will move focus out of the submenu and onto the parent MenuItem.
if ((role != MenuItemRole.TopLevelHeader) && (role != MenuItemRole.TopLevelItem))
{
if (IsSubmenuOpen)
{
IsSubmenuOpen = false;
handled = true;
}
}
}
break;
}
if (!handled)
{
// Use the unadulterated e.Key here because the later translation
// to FocusNavigationDirection takes this into account.
handled = MenuItemNavigate(e.Key, e.KeyboardDevice.Modifiers);
}
if (handled)
{
e.Handled = true;
}
}
///
/// The Access key for this control was invoked.
///
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnAccessKey(AccessKeyEventArgs e)
{
base.OnAccessKey(e);
if (!e.IsMultiple)
{
MenuItemRole type = Role;
switch (type)
{
case MenuItemRole.TopLevelItem:
case MenuItemRole.SubmenuItem:
{
ClickItem(e.UserInitiated);
}
break;
case MenuItemRole.TopLevelHeader :
case MenuItemRole.SubmenuHeader :
{
OpenSubmenuWithKeyboard();
}
break;
}
}
}
#pragma warning restore 0688
///
/// This method is invoked when the Items property changes.
///
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
// We use visual triggers to place the popup based on RoleProperty.
// Update the RoleProperty when Items property changes so popup can be placed accordingly.
UpdateRole();
base.OnItemsChanged(e);
}
///
/// Return true if the item is (or is eligible to be) its own ItemUI
///
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is MenuItem) || (item is Separator);
}
///
/// Determine whether the ItemContainerStyle/StyleSelector should apply to the item or not
///
protected override bool ShouldApplyItemContainerStyle(DependencyObject container, object item)
{
if (item is Separator)
{
return false;
}
else
{
return base.ShouldApplyItemContainerStyle(container, item);
}
}
/// Create or identify the element used to display the given item.
protected override DependencyObject GetContainerForItemOverride()
{
return new MenuItem();
}
///
/// Called when the parent of the Visual has changed.
///
/// Old parent or null if the Visual did not have a parent before.
protected internal override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
UpdateRole();
// Windows OS bug:1988393; DevDiv bug:107459
// MenuItem template contains ItemsPresenter where Grid.IsSharedSizeScope="true" and need to inherits PrivateSharedSizeScopeProperty value
// Property inheritance walk the locial tree if possible and skip the visual tree where ItemsPresenter is
// Workaround here will be to copy the property value from MenuItem visual parent
DependencyObject newParent = VisualTreeHelper.GetParentInternal(this);
// logical parent != null
// visual parent != null
// logical parent != visual parent <-- we are in the MenuItem is a logical child of a MenuItem case, not a data container case
// --- Set one-way binding with visual parent for DefinitionBase.PrivateSharedSizeScopeProperty
// NOTE: It seems impossible to get shared size scope to work in this hierarchical scenario
// under normal conditions, so putting this binding here without respecting an author's desire for
// shared size scope on the MenuItem container should be OK, since they wouldn't be able to
// get it to work anyway.
if (Parent != null && newParent != null && Parent != newParent)
{
Binding binding = new Binding();
binding.Path = new PropertyPath(DefinitionBase.PrivateSharedSizeScopeProperty);
binding.Mode = BindingMode.OneWay;
binding.Source = newParent;
BindingOperations.SetBinding(this, DefinitionBase.PrivateSharedSizeScopeProperty, binding);
}
// visual parent == null
// --- Clear binding for DefinitionBase.PrivateSharedSizeScopeProperty
if (newParent == null)
{
BindingOperations.ClearBinding(this, DefinitionBase.PrivateSharedSizeScopeProperty);
}
}
///
/// Called when the Template's tree has been generated
///
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (_submenuPopup != null)
{
_submenuPopup.Closed -= OnPopupClosed;
}
_submenuPopup = GetTemplateChild(PopupTemplateName) as Popup;
if (_submenuPopup != null)
{
_submenuPopup.Closed += OnPopupClosed;
}
}
#endregion
//-------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------
#region Private Methods
private void SetMenuMode(bool menuMode)
{
Debug.Assert(Role == MenuItemRole.TopLevelHeader || Role == MenuItemRole.TopLevelItem, "MenuItem was not top-level");
MenuBase parentMenu = LogicalParent as MenuBase;
if (parentMenu != null)
{
if (parentMenu.IsMenuMode != menuMode)
{
parentMenu.IsMenuMode = menuMode;
}
}
}
///
/// Returns true if the parent has capture. Does not work for submenu items/headers.
///
private bool IsInMenuMode
{
get
{
MenuBase parentMenu = LogicalParent as MenuBase;
if (parentMenu != null)
{
return parentMenu.IsMenuMode;
}
return false;
}
}
///
/// Returns true if the top level header should open when the mouse enters it
///
private bool OpenOnMouseEnter
{
get
{
MenuBase parentMenu = LogicalParent as MenuBase;
if (parentMenu != null)
{
Debug.Assert(!parentMenu.OpenOnMouseEnter || parentMenu.IsMenuMode, "OpenOnMouseEnter can only be true when IsMenuMode is true");
return parentMenu.OpenOnMouseEnter;
}
return false;
}
}
// This is so that MenuItems inside a ContextMenu can behave differently
internal static readonly DependencyProperty InsideContextMenuProperty
= DependencyProperty.RegisterAttached("InsideContextMenu", typeof(bool), typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
private bool InsideContextMenu
{
get
{
return (bool)GetValue(InsideContextMenuProperty);
}
}
internal static void SetInsideContextMenuProperty(UIElement element, bool value)
{
element.SetValue(InsideContextMenuProperty, BooleanBoxes.Box(value));
}
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
/// TreatAsSafe - passes false.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void ClickItem()
{
ClickItem(false);
}
///
/// Critical - Calls OnClickCore, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
private void ClickItem(bool userInitiated)
{
try
{
OnClickCore(userInitiated);
}
finally
{
// When you click a top-level item, we need to exit menu mode.
if (Role == MenuItemRole.TopLevelItem)
{
SetMenuMode(false);
}
}
}
internal void ClickHeader()
{
if (!IsKeyboardFocusWithin)
{
FocusOrSelect();
}
if (IsSubmenuOpen)
{
if (Role == MenuItemRole.TopLevelHeader)
{
SetMenuMode(false);
}
}
else
{
// Immediately open the menu when it's clicked. This will stop any
// timers to open or close the submenu.
OpenMenu();
}
}
internal bool OpenMenu()
{
if (!IsSubmenuOpen)
{
// Verify that the parent of the MenuItem is valid;
ItemsControl owner = ItemsControl.ItemsControlFromItemContainer(this);
if (owner == null)
{
owner = VisualTreeHelper.GetParent(this) as ItemsControl;
}
if ((owner != null) && ((owner is MenuItem) || (owner is MenuBase)))
{
// Parent must be MenuItem or MenuBase in order for menus to open.
// Otherwise, odd behavior will occur.
IsSubmenuOpen = true;
return true; // The value was actually changed
}
}
return false;
}
///
/// Set IsSubmenuOpen = true and select the first item.
///
internal void OpenSubmenuWithKeyboard()
{
SetBoolField(BoolField.OpenedWithKeyboard, true);
if (OpenMenu())
{
NavigateToStart(new ItemNavigateArgs(Keyboard.PrimaryDevice, Keyboard.Modifiers));
}
}
///
/// Navigate from one MenuItem to a sibling.
///
/// Raw key that was pressed (RTL is respected within this method).
///
/// true if navigation was successful.
private bool MenuItemNavigate(Key key, ModifierKeys modifiers)
{
if (key == Key.Left || key == Key.Right || key == Key.Up || key == Key.Down)
{
ItemsControl parent = ItemsControlFromItemContainer(this);
if (parent != null)
{
if (!parent.HasItems)
{
return false;
}
int count = parent.Items.Count;
// Optimize for the case where the submenu contains one item.
if (count == 1 && !(parent is Menu))
{
// Return true if we were navigating up/down (we cycled around).
if (key == Key.Up && key == Key.Down)
{
return true;
}
}
object previousFocus = Keyboard.FocusedElement;
parent.NavigateByLine(KeyboardNavigation.KeyToTraversalDirection(key), new ItemNavigateArgs(Keyboard.PrimaryDevice, modifiers));
object currentFocus = Keyboard.FocusedElement;
if ((currentFocus != previousFocus) && (currentFocus != this))
{
return true;
}
}
}
return false;
}
///
/// Returns logical parent; either Parent or ItemsControlFromItemContainer(this).
///
///
internal object LogicalParent
{
get
{
if (Parent != null)
{
return Parent;
}
return ItemsControlFromItemContainer(this);
}
}
///
/// Return the current sibling of this MenuItem -- the
/// CurrentSelection of the parent as long as it isn't us.
///
private MenuItem CurrentSibling
{
get
{
object parent = LogicalParent;
MenuItem menuItemParent = parent as MenuItem;
MenuItem sibling = null;
if (menuItemParent != null)
{
sibling = menuItemParent.CurrentSelection;
}
else
{
MenuBase menuParent = parent as MenuBase;
if (menuParent != null)
{
sibling = menuParent.CurrentSelection;
}
}
if (sibling == this)
{
sibling = null;
}
return sibling;
}
}
///
/// Returns true if the mouse is somewhere in the hierarchy
/// but not over this node. Note that this is slightly different
/// from CurrentSibling.IsMouseOver because there are regions in
/// the menu which are not occupied by siblings and we're interested
/// in that case too.
///
private bool IsMouseOverSibling
{
get
{
FrameworkElement parent = LogicalParent as FrameworkElement;
// If the mouse is over our parent but not over us, then
// the mouse must be somewhere in a sibling hierarchy.
//
// NOTE: If this check were changed to CurrentSibling.IsMouseOver
// then our behavior becomes identical to the behavior
// of the start menu, where a menu doesn't close unless
// you have settled on another hierarchy. Here we will
// close unless you are settled on this item's hierarchy.
if (parent != null && IsMouseReallyOver(parent) && !IsMouseOver)
{
return true;
}
return false;
}
}
///
/// Performs an IsMouseOver test but accounts for elements that have capture
/// and instead checks their children.
///
/// The element to test.
/// True if the mouse is over the element, regardless of capture. False otherwise.
private static bool IsMouseReallyOver(FrameworkElement elem)
{
bool isMouseOver = elem.IsMouseOver;
if (isMouseOver)
{
if ((Mouse.Captured == elem) && (Mouse.DirectlyOver == elem))
{
// The mouse is not over any of the children of this captured element.
// Assuming that this means that the mouse is not really over the element.
return false;
}
}
return isMouseOver;
}
///
/// Select this item and expand the hierarchy below it.
///
///
private void OpenHierarchy(MenuItemRole role)
{
FocusOrSelect();
if (role == MenuItemRole.TopLevelHeader || role == MenuItemRole.SubmenuHeader)
{
OpenMenu();
}
}
///
/// Focus this item or, if that fails, just mark it selected.
///
private void FocusOrSelect()
{
// Setting focus will cause the item to be selected,
// but if we fail to focus we should still select.
// (This is to help enable focusless menus).
// Check IsKeyboardFocusWithin to allow rich content within the menuitem.
if (!IsKeyboardFocusWithin)
{
Focus();
}
if (!IsSelected)
{
// If it's already focused, make sure it's also selected.
IsSelected = true;
}
// If the item is selected we should ensure that it's highlighted.
if (IsSelected && !IsHighlighted)
{
IsHighlighted = true;
}
}
private void SetTimerToOpenHierarchy()
{
if (_openHierarchyTimer == null)
{
_openHierarchyTimer = new DispatcherTimer(DispatcherPriority.Normal);
_openHierarchyTimer.Tick += (EventHandler)delegate(object sender, EventArgs e)
{
OpenHierarchy(Role);
StopTimer(ref _openHierarchyTimer);
};
}
else
{
_openHierarchyTimer.Stop();
}
StartTimer(_openHierarchyTimer);
}
private void SetTimerToCloseHierarchy()
{
if (_closeHierarchyTimer == null)
{
_closeHierarchyTimer = new DispatcherTimer(DispatcherPriority.Normal);
_closeHierarchyTimer.Tick += (EventHandler)delegate(object sender, EventArgs e)
{
// Deselect the item; will remove highlight and collapse hierarchy.
IsSelected = false;
StopTimer(ref _closeHierarchyTimer);
};
}
else
{
_closeHierarchyTimer.Stop();
}
StartTimer(_closeHierarchyTimer);
}
private void StopTimer(ref DispatcherTimer timer)
{
if (timer != null)
{
timer.Stop();
timer = null;
}
}
private void StartTimer(DispatcherTimer timer)
{
Debug.Assert(timer != null, "timer should not be null.");
Debug.Assert(!timer.IsEnabled, "timer should not be running.");
timer.Interval = TimeSpan.FromMilliseconds(SystemParameters.MenuShowDelay);
timer.Start();
}
private static object OnCoerceAcceleratorKey(DependencyObject d, object value)
{
if (value == null)
{
string inputGestureText = ((MenuItem)d).InputGestureText;
if (inputGestureText != String.Empty)
{
value = inputGestureText;
}
}
return value;
}
#endregion
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private Fields
///
/// Tracks the current selection in the items collection (i.e. submenu)
/// of this MenuItem.
///
private MenuItem CurrentSelection
{
get
{
return _currentSelection;
}
set
{
if (_currentSelection != null)
{
_currentSelection.IsSelected = false;
}
_currentSelection = value;
if (_currentSelection != null)
{
_currentSelection.IsSelected = true;
}
// NOTE: (Win32 disparity) If CurrentSelection changes to null
// and the focus was within the old CurrentSelection, we
// the parent should take focus back. In Win32 the "virtual"
// focus was tracked by way of the currently selected guy in
// If you were selected but none of your children were, you
// were effectively selected. It should be relatively easy to
// enable this behavior by checking if IsKeyboardFocusWithin is true
// on the previous child and then setting Focus to ourselves
// when _currentSelection becomes null. We would need to do this
// here and in MenuBase.CurrentSelection.
}
}
private bool GetBoolField(BoolField field)
{
return (_menuItemBoolFieldStore & field) != 0;
}
private void SetBoolField(BoolField field, bool value)
{
if (value)
{
_menuItemBoolFieldStore |= field;
}
else
{
_menuItemBoolFieldStore &= (~field);
}
}
[Flags]
private enum BoolField
{
OpenedWithKeyboard = 0x01,
IgnoreNextMouseLeave = 0x02,
IgnoreMouseEvents = 0x04,
MouseEnterOnMouseMove = 0x08,
CanExecuteInvalid = 0x10,
}
//
// This property
// 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject
// 2. This is a performance optimization
//
internal override int EffectiveValuesInitialSize
{
get { return 42; }
}
private bool CanExecute
{
get { return !ReadControlFlag(ControlBoolFlags.CommandDisabled); }
set
{
if (value != CanExecute)
{
WriteControlFlag(ControlBoolFlags.CommandDisabled, !value);
CoerceValue(IsEnabledProperty);
}
}
}
private const string PopupTemplateName = "PART_Popup";
private BoolField _menuItemBoolFieldStore;
private MenuItem _currentSelection;
private Popup _submenuPopup;
DispatcherTimer _openHierarchyTimer;
DispatcherTimer _closeHierarchyTimer;
// This is to hold onto a CanExecuteChanged event handler to respond to changes in a command's CanExecute property
private static readonly UncommonField CanExecuteChangedHandler = new UncommonField();
///
/// Critical: Setting this to true indicates that the mouse down was user initiated
///
[SecurityCritical]
private bool _userInitiatedPress;
#endregion
#region DTypeThemeStyleKey
// Returns the DependencyObjectType for the registered ThemeStyleKey's default
// value. Controls will override this method to return approriate types.
internal override DependencyObjectType DTypeThemeStyleKey
{
get { return _dType; }
}
private static DependencyObjectType _dType;
#endregion DTypeThemeStyleKey
#region ItemsStyleKey
///
/// Resource Key for the SeparatorStyle
///
public static ResourceKey SeparatorStyleKey
{
get
{
if (_cacheSeparatorStyle == null)
{
_cacheSeparatorStyle = new SystemThemeKey(SystemResourceKeyID.MenuItemSeparatorStyle);
}
return _cacheSeparatorStyle;
}
}
private static SystemThemeKey _cacheSeparatorStyle;
#endregion ItemsStyleKey
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Utility;
using System.Diagnostics;
using System.Windows.Threading;
using System.Globalization;
using System.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Security;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Shapes;
using System.Windows.Markup;
// Disable CS3001: Warning as Error: not CLS-compliant
#pragma warning disable 3001
namespace System.Windows.Controls
{
///
/// Defines the different placement types of MenuItems.
///
public enum MenuItemRole
{
///
/// A top-level menu item that can invoke commands.
///
TopLevelItem,
///
/// Header for top-level menus.
///
TopLevelHeader,
///
/// A menu item in a submenu that can invoke commands.
///
SubmenuItem,
///
/// A header for a submenu.
///
SubmenuHeader,
}
///
/// A child item of Menu.
/// MenuItems can be selected to invoke commands.
/// MenuItems can be headers for submenus.
/// MenuItems can be checked or unchecked.
///
[DefaultEvent("Click")]
[Localizability(LocalizationCategory.Menu)]
[TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(MenuItem))]
public class MenuItem : HeaderedItemsControl, ICommandSource
{
// ---------------------------------------------------------------------------
// Defines the names of the resources to be consumed by the MenuItem style.
// Used to restyle several roles of MenuItem without having to restyle
// all of the control.
// ---------------------------------------------------------------------------
#region StyleKeys
///
/// Key used to mark the template for use by TopLevel MenuItems
///
public static ResourceKey TopLevelItemTemplateKey
{
get
{
if (_topLevelItemTemplateKey == null)
{
_topLevelItemTemplateKey = new ComponentResourceKey(typeof(MenuItem), "TopLevelItemTemplateKey");
}
return _topLevelItemTemplateKey;
}
}
///
/// Key used to mark the template for use by TopLevel Menu Header
///
public static ResourceKey TopLevelHeaderTemplateKey
{
get
{
if (_topLevelHeaderTemplateKey == null)
{
_topLevelHeaderTemplateKey = new ComponentResourceKey(typeof(MenuItem), "TopLevelHeaderTemplateKey");
}
return _topLevelHeaderTemplateKey;
}
}
///
/// Key used to mark the template for use by Submenu Item
///
public static ResourceKey SubmenuItemTemplateKey
{
get
{
if (_submenuItemTemplateKey == null)
{
_submenuItemTemplateKey = new ComponentResourceKey(typeof(MenuItem), "SubmenuItemTemplateKey");
}
return _submenuItemTemplateKey;
}
}
///
/// Key used to mark the template for use by Submenu Header
///
public static ResourceKey SubmenuHeaderTemplateKey
{
get
{
if (_submenuHeaderTemplateKey == null)
{
_submenuHeaderTemplateKey = new ComponentResourceKey(typeof(MenuItem), "SubmenuHeaderTemplateKey");
}
return _submenuHeaderTemplateKey;
}
}
private static ComponentResourceKey _topLevelItemTemplateKey;
private static ComponentResourceKey _topLevelHeaderTemplateKey;
private static ComponentResourceKey _submenuItemTemplateKey;
private static ComponentResourceKey _submenuHeaderTemplateKey;
#endregion
//--------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------
#region Constructors
///
/// Default MenuItem constructor
///
public MenuItem() : base()
{
}
static MenuItem()
{
HeaderProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceHeader)));
EventManager.RegisterClassHandler(typeof(MenuItem), AccessKeyManager.AccessKeyPressedEvent, new AccessKeyPressedEventHandler(OnAccessKeyPressed));
EventManager.RegisterClassHandler(typeof(MenuItem), MenuBase.IsSelectedChangedEvent, new RoutedPropertyChangedEventHandler(OnIsSelectedChanged));
ForegroundProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemColors.MenuTextBrush));
FontFamilyProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily));
FontSizeProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontSize));
FontStyleProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontStyle));
FontWeightProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(SystemFonts.MessageFontWeight));
// Disable tooltips on menu item when submenu is open
ToolTipService.IsEnabledProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceToolTipIsEnabled)));
#if OLD_AUTOMATION
AutomationProvider.AcceleratorKeyProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(null, (PropertyChangedCallback)null, new CoerceValueCallback(OnCoerceAcceleratorKey)));
#endif
DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(typeof(MenuItem)));
_dType = DependencyObjectType.FromSystemTypeInternal(typeof(MenuItem));
KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(KeyboardNavigationMode.None));
// Disable default focus visual for MenuItem.
FocusVisualStyleProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata((object)null /* default value */));
// While the menu is opened, Input Method should be suspended.
// the docusmen focus of Cicero should not be changed but key typing should not be
// dispatched to IME/TIP.
InputMethod.IsInputMethodSuspendedProperty.OverrideMetadata(typeof(MenuItem), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
}
#endregion
//--------------------------------------------------------------------
//
// Public Events
//
//--------------------------------------------------------------------
#region Public Events
///
/// Event corresponds to left mouse button click
///
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Add / Remove Click handler
///
[Category("Behavior")]
public event RoutedEventHandler Click
{
add
{
AddHandler(MenuItem.ClickEvent, value);
}
remove
{
RemoveHandler(MenuItem.ClickEvent, value);
}
}
///
/// Event that is fired when mouse button is pressed down but before menus are closed.
/// This event should be handled by the parent menu and used to know when to close all submenus.
///
internal static readonly RoutedEvent PreviewClickEvent = EventManager.RegisterRoutedEvent("PreviewClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Checked event
///
public static readonly RoutedEvent CheckedEvent = EventManager.RegisterRoutedEvent("Checked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Unchecked event
///
public static readonly RoutedEvent UncheckedEvent = EventManager.RegisterRoutedEvent("Unchecked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Add / Remove Checked handler
///
[Category("Behavior")]
public event RoutedEventHandler Checked
{
add
{
AddHandler(CheckedEvent, value);
}
remove
{
RemoveHandler(CheckedEvent, value);
}
}
///
/// Add / Remove Unchecked handler
///
[Category("Behavior")]
public event RoutedEventHandler Unchecked
{
add
{
AddHandler(UncheckedEvent, value);
}
remove
{
RemoveHandler(UncheckedEvent, value);
}
}
///
/// Event fires when submenu opens
///
public static readonly RoutedEvent SubmenuOpenedEvent =
EventManager.RegisterRoutedEvent("SubmenuOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Event fires when submenu closes
///
public static readonly RoutedEvent SubmenuClosedEvent =
EventManager.RegisterRoutedEvent("SubmenuClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MenuItem));
///
/// Add / Remove SubmenuOpenedEvent handler
///
[Category("Behavior")]
public event RoutedEventHandler SubmenuOpened
{
add
{
AddHandler(SubmenuOpenedEvent, value);
}
remove
{
RemoveHandler(SubmenuOpenedEvent, value);
}
}
///
/// Add / Remove SubmenuClosedEvent handler
///
[Category("Behavior")]
public event RoutedEventHandler SubmenuClosed
{
add
{
AddHandler(SubmenuClosedEvent, value);
}
remove
{
RemoveHandler(SubmenuClosedEvent, value);
}
}
#endregion
//-------------------------------------------------------------------
//
// Public Properties
//
//--------------------------------------------------------------------
#region Public Properties
// Set the header to the command text if no header has been explicitly specified
private static object CoerceHeader(DependencyObject d, object value)
{
MenuItem menuItem = (MenuItem)d;
RoutedUICommand uiCommand;
// If no header has been set, use the command's text
if (value == null && !menuItem.HasNonDefaultValue(HeaderProperty))
{
uiCommand = menuItem.Command as RoutedUICommand;
if (uiCommand != null)
{
value = uiCommand.Text;
}
return value;
}
// If the header had been set to a UICommand by the ItemsControl, replace it with the command's text
uiCommand = value as RoutedUICommand;
if (uiCommand != null)
{
// The header is equal to the command.
// If this MenuItem was generated for the command, then go ahead and overwrite the header
// since the generator automatically set the header.
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(menuItem);
if (parent != null)
{
object originalItem = parent.ItemContainerGenerator.ItemFromContainer(menuItem);
if (originalItem == value)
{
return uiCommand.Text;
}
}
}
return value;
}
///
/// The DependencyProperty for the RoutedCommand.
/// Flags: None
/// Default Value: null
///
public static readonly DependencyProperty CommandProperty =
ButtonBase.CommandProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata(
(ICommand)null,
new PropertyChangedCallback(OnCommandChanged)));
///
/// The MenuItem's Command.
///
[Bindable(true), Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem item = (MenuItem) d;
item.OnCommandChanged((ICommand) e.OldValue, (ICommand) e.NewValue);
}
private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
{
if (oldCommand != null)
{
UnhookCommand(oldCommand);
}
if (newCommand != null)
{
HookCommand(newCommand);
}
CoerceValue(HeaderProperty);
CoerceValue(InputGestureTextProperty);
}
private void UnhookCommand(ICommand command)
{
EventHandler handler = CanExecuteChangedHandler.GetValue(this);
if (handler != null)
{
command.CanExecuteChanged -= handler;
CanExecuteChangedHandler.ClearValue(this);
}
UpdateCanExecute();
}
private void HookCommand(ICommand command)
{
EventHandler handler = new EventHandler(OnCanExecuteChanged);
CanExecuteChangedHandler.SetValue(this, handler);
command.CanExecuteChanged += handler;
UpdateCanExecute();
}
private void OnCanExecuteChanged(object sender, EventArgs e)
{
UpdateCanExecute();
}
private void UpdateCanExecute()
{
SetBoolField(BoolField.CanExecuteInvalid, false);
if (Command != null)
{
// Perf optimization - only raise CanExecute event if the menu is open
MenuItem parent = ItemsControl.ItemsControlFromItemContainer(this) as MenuItem;
if (parent == null || parent.IsSubmenuOpen)
{
CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this);
}
else
{
CanExecute = true;
SetBoolField(BoolField.CanExecuteInvalid, true);
}
}
else
{
CanExecute = true;
}
}
///
/// Fetches the value of the IsEnabled property
///
///
/// The reason this property is overridden is so that MenuItem
/// can infuse the value for CanExecute into it.
///
protected override bool IsEnabledCore
{
get
{
return base.IsEnabledCore && CanExecute;
}
}
///
/// The DependencyProperty for the RoutedCommand's parameter.
/// Flags: None
/// Default Value: null
///
public static readonly DependencyProperty CommandParameterProperty =
ButtonBase.CommandParameterProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata((object) null));
///
/// The parameter to pass to MenuItem's Command.
///
[Bindable(true), Category("Action")]
[Localizability(LocalizationCategory.NeverLocalize)]
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
///
/// The DependencyProperty for Target property
/// Flags: None
/// Default Value: null
///
public static readonly DependencyProperty CommandTargetProperty =
ButtonBase.CommandTargetProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata((IInputElement) null));
///
/// The target element on which to fire the command.
///
[Bindable(true), Category("Action")]
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
///
/// The DependencyProperty for the IsSubmenuOpen property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsSubmenuOpenProperty =
DependencyProperty.Register(
"IsSubmenuOpen",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnIsSubmenuOpenChanged),
new CoerceValueCallback(CoerceIsSubmenuOpen)));
///
/// When the MenuItem's submenu is visible.
///
[Bindable(true), Browsable(false), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsSubmenuOpen
{
get { return (bool) GetValue(IsSubmenuOpenProperty); }
set { SetValue(IsSubmenuOpenProperty, BooleanBoxes.Box(value)); }
}
private static object CoerceIsSubmenuOpen(DependencyObject d, object value)
{
if ((bool) value)
{
MenuItem mi = (MenuItem) d;
if (!mi.IsLoaded)
{
mi.RegisterToOpenOnLoad();
return BooleanBoxes.FalseBox;
}
}
return value;
}
// Disable tooltips on opened menu items
private static object CoerceToolTipIsEnabled(DependencyObject d, object value)
{
MenuItem mi = (MenuItem) d;
return mi.IsSubmenuOpen ? BooleanBoxes.FalseBox : value;
}
private void RegisterToOpenOnLoad()
{
Loaded += new RoutedEventHandler(OpenOnLoad);
}
private void OpenOnLoad(object sender, RoutedEventArgs e)
{
// Open menu after it has rendered (Loaded is fired before 1st render)
Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
{
CoerceValue(IsSubmenuOpenProperty);
return null;
}), null);
}
///
/// Called when IsSubmenuOpenID is invalidated on "d."
///
private static void OnIsSubmenuOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem menuItem = (MenuItem)d;
bool oldValue = (bool) e.OldValue;
bool newValue = (bool) e.NewValue;
// The IsSubmenuOpen value has changed; this should stop any timers
// we may have set to open/close the menus.
menuItem.StopTimer(ref menuItem._openHierarchyTimer);
menuItem.StopTimer(ref menuItem._closeHierarchyTimer);
MenuItemAutomationPeer peer = UIElementAutomationPeer.FromElement(menuItem) as MenuItemAutomationPeer;
if (peer != null)
{
peer.ResetChildrenCache();
peer.RaiseExpandCollapseAutomationEvent(oldValue, newValue);
}
if (newValue)
{
CommandManager.InvalidateRequerySuggested(); // Should post an idle queue item to update IsEnabled on commands
// When menuitem's submenu opens, it should be selected.
menuItem.IsSelected = true;
MenuItemRole role = menuItem.Role;
if (role == MenuItemRole.TopLevelHeader)
{
menuItem.SetMenuMode(true);
}
menuItem.CurrentSelection = null;
// When our submenu opens, update our siblings so they do not animate
menuItem.NotifySiblingsToSuspendAnimation();
// Force update of CanExecute when opening menu.
for (int i = 0; i < menuItem.Items.Count; i++)
{
MenuItem subItem = menuItem.ItemContainerGenerator.ContainerFromIndex(i) as MenuItem;
if (subItem != null && subItem.GetBoolField(BoolField.CanExecuteInvalid))
{
subItem.UpdateCanExecute();
}
}
menuItem.OnSubmenuOpened(new RoutedEventArgs(SubmenuOpenedEvent, menuItem));
menuItem.SetBoolField(BoolField.IgnoreMouseEvents, true);
menuItem.SetBoolField(BoolField.MouseEnterOnMouseMove, false);
// MenuItem should ignore any mouse enter or move events until the menu has fully
// opened. Otherwise we may highlight a menu item under the mouse even though
// the user opened the menu with the keyboard
// This is fired below input priority so any mouse events happen before setting the flag
menuItem.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate(object param)
{
menuItem.SetBoolField(BoolField.IgnoreMouseEvents, false);
return null;
}), null);
}
else
{
// Our submenu is closing, so close our submenu's submenu
if (menuItem.CurrentSelection != null)
{
// We're about to close the submenu -- if focus is within
// the subtree, we need to take it back so that Focus isn't
// left in an orphaned tree.
if (menuItem.CurrentSelection.IsKeyboardFocusWithin)
{
menuItem.Focus();
}
if (menuItem.CurrentSelection.IsSubmenuOpen)
{
menuItem.CurrentSelection.IsSubmenuOpen = false;
}
}
else
{
// We need to take focus out of the subtree if we close
// the submenu. Above we can be sure that focus will be
// on the selected item so we just need to check if IsFocusWithin
// is true on the selected item. If we have no CurrentSelection,
// we have to be a little more aggressive and take focus
// back if IsFocusWithin is true.
//
// NOTE: This could potentially steal focus back from something
// within the menuitem's header (say, a TextBox) but it is
// unlikely that focus will be within a header while the submenu
// is open.
if (menuItem.IsKeyboardFocusWithin)
{
if (!menuItem.Focus())
{
// Shoot, we couldn't take focus out of the submenu
// and put it back on ourselves. Now focus is in a
// disconnected subtree. Ultimately core input will
// disallow this, presumably by setting focus to null.
// For now we won't handle this case.
}
}
}
menuItem.CurrentSelection = null;
if ((menuItem.IsMouseOver) && (menuItem.Role == MenuItemRole.SubmenuHeader))
{
// If the mouse is inside the subtree, then we will get a mouse leave, but we want to ignore it
// to maintain the highlight.
menuItem.SetBoolField(BoolField.IgnoreNextMouseLeave, true);
}
// When our submenu closes, update our children so they will animate
menuItem.NotifyChildrenToResumeAnimation();
// No Popup in the style so fire closed now
if (menuItem._submenuPopup == null)
{
menuItem.OnSubmenuClosed(new RoutedEventArgs(SubmenuClosedEvent, menuItem));
}
}
menuItem.CoerceValue(ToolTipService.IsEnabledProperty);
}
private void OnPopupClosed(object source, EventArgs e)
{
OnSubmenuClosed(new RoutedEventArgs(SubmenuClosedEvent, this));
}
///
///
///
///
protected virtual void OnSubmenuOpened(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
///
///
///
protected virtual void OnSubmenuClosed(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// The key needed set a read-only property.
///
private static readonly DependencyPropertyKey RolePropertyKey =
DependencyProperty.RegisterReadOnly(
"Role",
typeof(MenuItemRole),
typeof(MenuItem),
new FrameworkPropertyMetadata(MenuItemRole.TopLevelItem));
///
/// The DependencyProperty for the Role property.
/// Flags: None
/// Default Value: MenuItemRole.TopLevelItem
///
public static readonly DependencyProperty RoleProperty =
RolePropertyKey.DependencyProperty;
///
/// What the role of the menu item is: TopLevelItem, TopLevelHeader, SubmenuItem, SubmenuHeader.
///
[Category("Behavior")]
public MenuItemRole Role
{
get { return (MenuItemRole) GetValue(RoleProperty); }
}
private void UpdateRole()
{
MenuItemRole type;
if (!IsCheckable && HasItems)
{
if (LogicalParent is Menu)
{
type = MenuItemRole.TopLevelHeader;
}
else
{
type = MenuItemRole.SubmenuHeader;
}
}
else
{
if (LogicalParent is Menu)
{
type = MenuItemRole.TopLevelItem;
}
else
{
type = MenuItemRole.SubmenuItem;
}
}
SetValue(RolePropertyKey, type);
}
///
/// The DependencyProperty for the IsCheckable property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsCheckableProperty =
DependencyProperty.Register(
"IsCheckable",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
new PropertyChangedCallback(OnIsCheckableChanged)));
///
/// IsCheckable determines the user ability to check/uncheck the item.
///
[Bindable(true), Category("Behavior")]
public bool IsCheckable
{
get { return (bool)GetValue(IsCheckableProperty); }
set { SetValue(IsCheckableProperty, value); }
}
private static void OnIsCheckableChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
((MenuItem) target).UpdateRole();
}
///
/// The DependencyPropertyKey for the IsPressed property.
/// Flags: None
/// Default Value: false
///
private static readonly DependencyPropertyKey IsPressedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsPressed",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// The DependencyProperty for the IsPressed property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty;
///
/// When the MenuItem is pressed.
///
[Browsable(false), Category("Appearance")]
public bool IsPressed
{
get { return (bool) GetValue(IsPressedProperty); }
protected set { SetValue(IsPressedPropertyKey, BooleanBoxes.Box(value)); }
}
private void UpdateIsPressed()
{
Rect itemBounds = new Rect(new Point(), RenderSize);
if ((Mouse.LeftButton == MouseButtonState.Pressed) &&
IsMouseOver &&
itemBounds.Contains(Mouse.GetPosition(this)))
{
IsPressed = true;
}
else
{
ClearValue(IsPressedPropertyKey);
}
}
///
/// The key needed set a read-only property.
///
private static readonly DependencyPropertyKey IsHighlightedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsHighlighted",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// The DependencyProperty for the IsHighlighted property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsHighlightedProperty =
IsHighlightedPropertyKey.DependencyProperty;
///
/// Whether the MenuItem should be highlighted.
///
[Browsable(false), Category("Appearance")]
public bool IsHighlighted
{
get { return (bool) GetValue(IsHighlightedProperty); }
protected set { SetValue(IsHighlightedPropertyKey, BooleanBoxes.Box(value)); }
}
///
/// The DependencyProperty for the IsChecked property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register(
"IsChecked",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
new PropertyChangedCallback(OnIsCheckedChanged)));
///
/// When the MenuItem is checked.
///
[Bindable(true), Category("Appearance")]
public bool IsChecked
{
get { return (bool) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, BooleanBoxes.Box(value)); }
}
///
/// Called when IsChecked becomes true.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
protected virtual void OnChecked(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// Called when IsChecked becomes false.
///
/// Event arguments for the routed event that is raised by the default implementation of this method.
protected virtual void OnUnchecked(RoutedEventArgs e)
{
RaiseEvent(e);
}
///
/// Called when IsCheckedProperty is invalidated on "d."
///
private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem menuItem = (MenuItem) d;
if ((bool) e.NewValue)
{
menuItem.OnChecked(new RoutedEventArgs(CheckedEvent));
}
else
{
menuItem.OnUnchecked(new RoutedEventArgs(UncheckedEvent));
}
}
///
/// The DependencyProperty for the StaysOpenOnClick property.
/// Flags: None
/// Default Value: false
///
public static readonly DependencyProperty StaysOpenOnClickProperty =
DependencyProperty.Register(
"StaysOpenOnClick",
typeof(bool),
typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// Indicates that the submenu that this MenuItem is within should not close when this item is clicked.
///
[Bindable(true), Category("Behavior")]
public bool StaysOpenOnClick
{
get { return (bool) GetValue(StaysOpenOnClickProperty); }
set { SetValue(StaysOpenOnClickProperty, BooleanBoxes.Box(value)); }
}
///
/// True if this MenuItem is the current MenuItem of its parent.
/// Focus drives Selection, but not vice versa. This will enable
/// focusless menus.
///
internal bool IsSelected
{
get { return (bool) GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, BooleanBoxes.Box(value)); }
}
///
/// DependencyProperty for IsSelected property.
///
internal static readonly DependencyProperty IsSelectedProperty =
Selector.IsSelectedProperty.AddOwner(
typeof(MenuItem),
new FrameworkPropertyMetadata(
BooleanBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnIsSelectedChanged)));
private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MenuItem menuItem = (MenuItem)d;
// When IsSelected changes, IsHighlighted should reflect IsSelected
// Note: it is okay for IsHighlighted and IsSelected to be different.
// Selection and highlight will separate when mousing around in
// a submenu when any timers are active. Until you hover long
// enough and your selection is "committed", selection and highlight
// can disagree.
menuItem.SetValue(IsHighlightedPropertyKey, e.NewValue);
// If IsSelected is changing to false, make sure to close
// our submenu before doing anything.
if ((bool) e.OldValue)
{
if (menuItem.IsSubmenuOpen)
{
menuItem.IsSubmenuOpen = false;
}
// Also stop any timers immediately when we become deselected.
menuItem.StopTimer(ref menuItem._openHierarchyTimer);
menuItem.StopTimer(ref menuItem._closeHierarchyTimer);
}
menuItem.RaiseEvent(new RoutedPropertyChangedEventArgs((bool) e.OldValue, (bool) e.NewValue, MenuBase.IsSelectedChangedEvent));
}
///
/// Called when IsSelected changed on this element or any descendant.
///
private static void OnIsSelectedChanged(object sender, RoutedPropertyChangedEventArgs e)
{
// If IsSelected changed on a child of the MenuItem, change CurrentSelection
// to the element that sent the event and handle the event.
if (sender != e.OriginalSource)
{
MenuItem menuItem = (MenuItem)sender;
MenuItem source = e.OriginalSource as MenuItem;
if (source != null)
{
if (e.NewValue)
{
// If the item is now selected, we should stop any timers which will
// close the submenu. This is for the case where one mouses out of
// the current selection but then comes back.
if (menuItem.CurrentSelection == source)
{
menuItem.StopTimer(ref menuItem._closeHierarchyTimer);
}
// If the MenuItem is selected and it's a new item that's a child of ours,
// change the CurrentSelection.
if (menuItem.CurrentSelection != source && source.LogicalParent == menuItem)
{
if (menuItem.CurrentSelection != null && menuItem.CurrentSelection.IsSubmenuOpen)
{
menuItem.CurrentSelection.IsSubmenuOpen = false;
}
menuItem.CurrentSelection = source;
}
}
else
{
// If the item is no longer selected
// If the MenuItem has been deselected and it's the CurrentSelection,
// set our CurrentSelection to null.
if (menuItem.CurrentSelection == source)
{
menuItem.CurrentSelection = null;
}
}
// Mark the event as handled as long as it came from a MenuItem underneath us
// even if we didn't necessarily do anything.
e.Handled = true;
}
}
}
///
/// The DependencyProperty for the InputGestureText property.
/// Default Value: String.Empty
///
public static readonly DependencyProperty InputGestureTextProperty =
DependencyProperty.Register(
"InputGestureText",
typeof(string),
typeof(MenuItem),
new FrameworkPropertyMetadata(String.Empty,
new PropertyChangedCallback(OnInputGestureTextChanged),
new CoerceValueCallback(CoerceInputGestureText)));
///
/// Text describing an input gesture that will invoke the command tied to this item.
///
[Bindable(true), CustomCategory("Content")]
public string InputGestureText
{
get { return (string) GetValue(InputGestureTextProperty); }
set { SetValue(InputGestureTextProperty, value); }
}
private static void OnInputGestureTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
#if OLD_AUTOMATION
d.CoerceValue(AutomationProvider.AcceleratorKeyProperty);
#endif
}
// Gets the input gesture text from the command text if it hasn't been explicitly specified
private static object CoerceInputGestureText(DependencyObject d, object value)
{
MenuItem menuItem = (MenuItem)d;
RoutedCommand routedCommand;
if (String.IsNullOrEmpty((string)value) && !menuItem.HasNonDefaultValue(InputGestureTextProperty)
&& (routedCommand = menuItem.Command as RoutedCommand) != null )
{
InputGestureCollection col = routedCommand.InputGestures;
if ((col != null) && (col.Count >= 1))
{
// Search for the first key gesture
for (int i = 0; i < col.Count; i++)
{
KeyGesture keyGesture = ((IList)col)[i] as KeyGesture;
if (keyGesture != null)
{
return keyGesture.GetDisplayStringForCulture(CultureInfo.CurrentCulture);
}
}
}
}
return value;
}
///
/// The DependencyProperty for the Icon property.
/// Default Value: null
///
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(
"Icon",
typeof(object),
typeof(MenuItem),
new FrameworkPropertyMetadata((object)null));
///
/// Text describing an input gesture that will invoke the command tied to this item.
///
[Bindable(true), CustomCategory("Content")]
public object Icon
{
get { return GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
// This is used to disable animations after the menu has displayed once
private static readonly DependencyPropertyKey IsSuspendingPopupAnimationPropertyKey
= DependencyProperty.RegisterReadOnly("IsSuspendingPopupAnimation", typeof(bool), typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox));
///
/// Returns true if the Menu should suspend animations on its popup
///
public static readonly DependencyProperty IsSuspendingPopupAnimationProperty = IsSuspendingPopupAnimationPropertyKey.DependencyProperty;
///
/// Returns true if the Menu should suspend animations on its popup
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsSuspendingPopupAnimation
{
get
{
return (bool)GetValue(IsSuspendingPopupAnimationProperty);
}
internal set
{
SetValue(IsSuspendingPopupAnimationPropertyKey, BooleanBoxes.Box(value));
}
}
// When opening the menu item, tell all other menu items at the same
// level that their submenus should not animate
private void NotifySiblingsToSuspendAnimation()
{
// Don't need to set this property if it is already false
if (!IsSuspendingPopupAnimation)
{
bool openedWithKeyboard = GetBoolField(BoolField.OpenedWithKeyboard);
// When opened by the keyboard, don't animate - set menumode on all items
// otherwise ignore this MenuItem so it animates when opening
MenuItem ignore = openedWithKeyboard ? null : this;
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(this);
MenuBase.SetSuspendingPopupAnimation(parent, ignore, true);
if (!openedWithKeyboard)
{
// Delay setting InMenuMode on this until after bindings have done their
// work and opened the popup (if it exists)
Dispatcher.BeginInvoke(DispatcherPriority.Input,
(DispatcherOperationCallback)delegate(object arg)
{
((MenuItem)arg).IsSuspendingPopupAnimation = true;
return null;
},
this);
}
else
{
SetBoolField(BoolField.OpenedWithKeyboard, false);
}
}
}
// Set IsSuspendingAnimation=false on all our children
private void NotifyChildrenToResumeAnimation()
{
MenuBase.SetSuspendingPopupAnimation(this, null, false);
}
#endregion
//-------------------------------------------------------------------
//
// Protected Methods
//
//-------------------------------------------------------------------
#region Protected Methods
///
/// Creates AutomationPeer ( )
///
protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
{
return new System.Windows.Automation.Peers.MenuItemAutomationPeer(this);
}
///
/// This virtual method in called when IsInitialized is set to true and it raises an Initialized event
///
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
UpdateRole();
#if OLD_AUTOMATION
CoerceValue(AutomationProvider.AcceleratorKeyProperty);
#endif
}
///
/// Prepare the element to display the item. This may involve
/// applying styles, setting bindings, etc.
///
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
MenuItem.PrepareMenuItem(element, item);
}
///
/// Automatically set the Command property if the data item that this MenuItem represents is a command.
///
internal static void PrepareMenuItem(DependencyObject element, object item)
{
MenuItem menuItem = element as MenuItem;
if (menuItem != null)
{
ICommand command = item as ICommand;
if (command != null)
{
if (!menuItem.HasNonDefaultValue(CommandProperty))
{
menuItem.Command = command;
}
}
if (menuItem.GetBoolField(BoolField.CanExecuteInvalid))
{
menuItem.UpdateCanExecute();
}
}
else
{
Separator separator = item as Separator;
if (separator != null)
{
bool hasModifiers;
BaseValueSourceInternal vs = separator.GetValueSource(StyleProperty, null, out hasModifiers);
if (vs <= BaseValueSourceInternal.ImplicitReference)
separator.SetResourceReference(StyleProperty, SeparatorStyleKey);
separator.DefaultStyleKey = SeparatorStyleKey;
}
}
}
///
/// This virtual method in called when the MenuItem is clicked and it raises a Click event
///
///
/// Critical - Calls OnClickImple which sets the userInitiated bit on a command, which is used
/// for security purposes later.
/// TreatAsSafe - passes false for userInitiated
///
[SecurityCritical, SecurityTreatAsSafe]
protected virtual void OnClick()
{
OnClickImpl(false);
}
///
/// Critical - accepts a parameter which may be used to set the userInitiated
/// bit on a command, which is used for security purposes later.
///
[SecurityCritical]
internal virtual void OnClickCore(bool userInitiated)
{
OnClick();
}
///
/// Critical - Calls InvokeClickAfterRender which sets the userInitiated
/// bit on a command, which is used for security purposes later.
///
[SecurityCritical]
internal void OnClickImpl(bool userInitiated)
{
if (IsCheckable)
{
IsChecked = !IsChecked;
}
// Sub menu items will always be focused if they are moused over or keyboard navigated onto.
// When you click on a top-level menu item it should take focus.
// Sub menu items will not be focused if the mouse has moved out of
// the active hierarchy and has not settled on a new hierarchy yet.
if (!IsKeyboardFocusWithin)
{
FocusOrSelect();
}
// Raise the preview click. This will be handled by the parent menu and cause this submenu to disappear.
// It will also block until render-priority queue items have completed.
RaiseEvent(new RoutedEventArgs(MenuItem.PreviewClickEvent, this));
// Raise the automation event first *before* raising the Click event -
// otherwise automation may not get the event until after raising the click
// event returns, which could be problematic if the handler for that event
// displayed a modal dialog or did other significant work.
if (AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this);
if (peer != null)
peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
}
// We have just caused all the popup windows to be hidden and queued for async
// destroy (at < render priority). Hiding the window will cause the underlying windows
// to be queued for repaint -- we need to wait for any windows in our context to repaint.
Dispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(InvokeClickAfterRender), userInitiated);
}
///
/// Critical - sets the userInitiated bit on a command, which is used
/// for security purposes later.
///
[SecurityCritical]
private object InvokeClickAfterRender(object arg)
{
bool userInitiated = (bool)arg;
RaiseEvent(new RoutedEventArgs(MenuItem.ClickEvent, this));
MS.Internal.Commands.CommandHelpers.CriticalExecuteCommandSource(this, userInitiated);
return null;
}
///
/// Called when the left mouse button is pressed.
///
///
///
/// Critical: Sets an internal variable in case input was user initiated and button was pressed
/// TreatAsSafe: The variable is not exposed and there are link demands to block public callers
///
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
[SecurityCritical,SecurityTreatAsSafe]
#pragma warning disable 0688
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseDown(e);
UpdateIsPressed();
if (e.UserInitiated)
{
_userInitiatedPress = true;
}
}
base.OnMouseLeftButtonDown(e);
}
///
/// Called when the right mouse button is pressed.
///
///
///
/// Critical: Sets an internal variable in case input was user initiated and button was pressed
/// TreatAsSafe: The variable is not exposed and there are link demands to block public callers
///
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
[SecurityCritical,SecurityTreatAsSafe]
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseDown(e);
if (e.UserInitiated)
{
_userInitiatedPress = true;
}
}
base.OnMouseRightButtonDown(e);
}
///
/// Called when the left mouse button is released.
///
///
///
/// Critical - This code can be used to cause paste to occur programmatically.
///
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.InheritanceDemand , Unrestricted=true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseUp(e);
UpdateIsPressed();
_userInitiatedPress = false;
}
base.OnMouseLeftButtonUp(e);
}
///
/// Called when the right mouse button is released.
///
///
///
/// Critical - This code can be used to cause paste to occur programmatically.
///
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.InheritanceDemand , Unrestricted=true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
{
if (!e.Handled)
{
HandleMouseUp(e);
_userInitiatedPress = false;
}
base.OnMouseRightButtonUp(e);
}
#pragma warning restore 0688
private void HandleMouseDown(MouseButtonEventArgs e)
{
// ((0, 0), RenderSize) is the closest we can get to checking if the
// mouse event was on the header portion of the MenuItem (i.e. not on
// any part of the submenu)
Rect r = new Rect(new Point(), RenderSize);
if (r.Contains(e.GetPosition(this)))
{
if (e.ChangedButton == MouseButton.Left || (e.ChangedButton == MouseButton.Right && InsideContextMenu))
{
// Click happens on down for headers
MenuItemRole role = Role;
if (role == MenuItemRole.TopLevelHeader || role == MenuItemRole.SubmenuHeader)
{
ClickHeader();
}
}
}
// Handle mouse messages b/c they were over me, I just didn't use it
e.Handled = true;
}
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
private void HandleMouseUp(MouseButtonEventArgs e)
{
// See comment above in HandleMouseDown.
Rect r = new Rect(new Point(), RenderSize);
if (r.Contains(e.GetPosition(this)))
{
if (e.ChangedButton == MouseButton.Left || (e.ChangedButton == MouseButton.Right && InsideContextMenu))
{
// Click happens on up for items
MenuItemRole role = Role;
if (role == MenuItemRole.TopLevelItem || role == MenuItemRole.SubmenuItem)
{
if (_userInitiatedPress == true)
{
ClickItem(e.UserInitiated);
}
else
{
// This is the case where the mouse down happend on a different element
// but the moust up is happening on the menuitem. this is to prevent spoofing
// attacks where someone substitutes an element with a menu item
ClickItem(false);
}
}
//
/*
// Click happens on up for top level items that are already open
if (role == MenuItemRole.TopLevelHeader && IsSubmenuOpen)
{
ClickHeader();
e.Handled = true;
}
*/
}
}
if (e.ChangedButton != MouseButton.Right || InsideContextMenu)
{
// Handle all clicks unless there's a possibility of a ContextMenu inside a Menu.
e.Handled = true;
}
}
private static void OnAccessKeyPressed(object sender, AccessKeyPressedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
bool isScope = false;
if (e.Target == null)
{
// MenuItem access key should not work if something else beside MenuBase has capture
if (Mouse.Captured == null || Mouse.Captured is MenuBase)
{
e.Target = menuItem;
// special case is if we are the original source and our submenu is open,
// this is the case where the mouse moved over the header and focus is on
// the menu item but really you want to access key processing to be in your
// submenu.
// This assumes that no one will ever directly register a MenuItem with the AKM.
if (e.OriginalSource == menuItem && menuItem.IsSubmenuOpen)
{
isScope = true;
}
}
else
{
e.Handled = true;
}
}
else if (e.Scope == null)
{
// We want menu items to be a scope, but not for any AKs in its header.
// If e.Target is already filled in, check if it's a MenuItem.
// If it is and it's not us, we are its scope (i.e. we're the first MenuItem
// above it in the chain). If it's not a MenuItem, we have to take the long way.
if (e.Target != menuItem && e.Target is MenuItem)
{
isScope = true;
}
else
{
// This case handles when you have some non-MenuItem in a menu that can be
// the target of access keys, like a Button.
// MenuItems are a scope for all access keys which are outside of themselves.
// e.Source is the logical element in which the event was raised.
// If we can walk from the source to ourselves, then we are not correct
// scope of this access key; some parent should be.
DependencyObject source = e.Source as DependencyObject;
while (source != null)
{
// If we walk up to this Menuitem, we are not the scope.
if (source == menuItem)
{
break;
}
UIElement uiElement = source as UIElement;
// If we walk up to an item which is one of our children, we are their scope.
if ((uiElement != null) && (ItemsControlFromItemContainer(uiElement) == menuItem))
{
isScope = true;
break;
}
source = GetFrameworkParent(source);
}
}
}
if (isScope)
{
e.Scope = menuItem;
e.Handled = true;
}
}
///
/// An event reporting the mouse entered or left this element.
///
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
MenuItemRole role = Role;
// When we're a top-level menuitem we have to check if the menu has capture.
// If it doesn't we fall to the else below where we are just mousing around
// the top-level menuitems.
// (Note that Submenu items/headers do not have to look for capture.)
if (((role == MenuItemRole.TopLevelHeader || role == MenuItemRole.TopLevelItem) && IsInMenuMode)
|| (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.SubmenuItem))
{
MouseLeaveInMenuMode(role);
}
else
{
// Here we don't have capture and we're just mousing over
// top-level menu items. IsSelected should correspond to IsMouseOver.
if (IsMouseOver != IsSelected)
{
IsSelected = IsMouseOver;
}
}
UpdateIsPressed();
}
///
/// This is the method that responds to the MouseEvent event.
///
protected override void OnMouseMove(MouseEventArgs e)
{
// Ignore any mouse moves on ourselves while the popup is opening.
MenuItem parent = ItemsControl.ItemsControlFromItemContainer(this) as MenuItem;
if (parent != null &&
parent.GetBoolField(BoolField.MouseEnterOnMouseMove))
{
parent.SetBoolField(BoolField.MouseEnterOnMouseMove, false);
MouseEnterHelper();
}
}
///
/// An event reporting the mouse entered or left this element.
///
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
MouseEnterHelper();
}
private void MouseEnterHelper()
{
MenuItem parent = ItemsControl.ItemsControlFromItemContainer(this) as MenuItem;
// Do not enter and highlight this item until the popup has opened
// This prevents immediately selecting a submenu item when opening the menu
// because the mouse was already where the menu item appeared
if (parent == null || !parent.GetBoolField(BoolField.IgnoreMouseEvents))
{
MenuItemRole role = Role;
// When we're a top-level menuitem we have to check if the menu has capture.
// If it doesn't we fall to the else below where we are just mousing around
// the top-level menuitems.
// (Note that Submenu items/headers do not have to look for capture.)
if (((role == MenuItemRole.TopLevelHeader || role == MenuItemRole.TopLevelItem) && OpenOnMouseEnter)
|| (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.SubmenuItem))
{
MouseEnterInMenuMode(role);
}
else
{
// Here we don't have capture and we're just mousing over
// top-level menu items. IsSelected should correspond to IsMouseOver.
if (IsMouseOver != IsSelected)
{
IsSelected = IsMouseOver;
}
}
UpdateIsPressed();
}
else
{
parent.SetBoolField(BoolField.MouseEnterOnMouseMove, true);
}
}
private void MouseEnterInMenuMode(MenuItemRole role)
{
switch (role)
{
case MenuItemRole.TopLevelHeader:
case MenuItemRole.TopLevelItem:
{
// When mousing over a top-level hierarchy, it should open immediately.
if (!IsSubmenuOpen)
{
OpenHierarchy(role);
}
}
break;
case MenuItemRole.SubmenuHeader:
case MenuItemRole.SubmenuItem:
{
// If the current sibling has an open hierarchy, we cannot
// move focus/selection immediately. Instead we must set
// a timer to open after MenuShowDelay ms. If the sibling has
// no hierarchy open, it is safe to select the item immediately.
MenuItem sibling = CurrentSibling;
if (sibling == null || !sibling.IsSubmenuOpen)
{
if (!IsSubmenuOpen)
{
// Try to focus/select this item.
FocusOrSelect();
}
else
{
// If the submenu is open, then it should already be selected.
Debug.Assert(IsSelected, "When IsSubmenuOpen = true, IsSelected should be true as well");
// Need to make sure that when we leave the hierarchy and come back
// that the item is highlighted.
IsHighlighted = true;
}
}
else
{
// Highlight this item and remove the highlight
// from its sibling selected MenuItem
sibling.IsHighlighted = false;
IsHighlighted = true;
}
// If the submenu isn't open already, OpenHierarchy after MenuShowDelay ms
if (!IsSelected || !IsSubmenuOpen)
{
// When the timout happens, OpenHierarchy will select this item
SetTimerToOpenHierarchy();
}
}
break;
}
// Now that we're over this menu hierarchy with the mouse, we
// should stop any timers which might cause this hierarchy to close.
StopTimer(ref _closeHierarchyTimer);
}
private void MouseLeaveInMenuMode(MenuItemRole role)
{
// When mouse moves out of a submenu item, we should deselect
// the item. This is what Win32 does, and our menus don't
// feel right without it.
if (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.SubmenuItem)
{
if (GetBoolField(BoolField.IgnoreNextMouseLeave))
{
// The mouse was within a submenu that closed. A submenu header is receiving this
// message, but we want to ignore this one.
SetBoolField(BoolField.IgnoreNextMouseLeave, false);
}
else
{
if (!IsSubmenuOpen)
{
// When the submenu isn't open we can deselect the item right away.
if (IsSelected)
{
IsSelected = false;
}
else
{
// If it's not selected it might just be highlighted,
// so remove the highlight.
IsHighlighted = false;
}
if (IsKeyboardFocusWithin)
{
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(this);
if (parent != null)
{
parent.Focus();
}
}
}
else
{
// If the submenu is open and the mouse moved to some sibling
// hierarchy, we need to delay and deselect the item after
// MenuShowDelay ms, as long as the item doesn't get re-selected.
if (IsMouseOverSibling)
{
SetTimerToCloseHierarchy();
}
}
}
}
// No matter what, we've left the menu item and we should
// stop any timer which would cause the item to open.
StopTimer(ref _openHierarchyTimer);
}
///
/// An event announcing that the keyboard is focused on this element.
///
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
// Focus drives selection. If a MenuItem is focused, it should
// select itself.
if (!e.Handled && e.NewFocus == this)
{
IsSelected = true;
}
}
///
/// Called when the focus is no longer on or within this element.
///
protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsKeyboardFocusWithinChanged(e);
if (IsKeyboardFocusWithin && !IsSelected)
{
// If an item within us got focus (probably programatically), we need to become selected
IsSelected = true;
}
}
///
/// If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true.
/// Then ScrollViewer will not handle keyboard input and leave it up to the control.
///
protected internal override bool HandlesScrolling
{
get { return true; }
}
///
/// This is the method that responds to the KeyDown event.
///
/// Event arguments
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
//we get a compiler warning that a similar LinkDemand should exist on the base class
//but we don't want or need that. The base class doesn't cause user initiated paste to happen so it's safe to
//call programatically.
#pragma warning disable 0688
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
bool handled = false;
Key key = e.Key;
MenuItemRole role = Role;
FlowDirection flowDirection = FlowDirection;
// In Right to Left mode we switch Right and Left keys
if (flowDirection == FlowDirection.RightToLeft)
{
if (key == Key.Right)
{
key = Key.Left;
}
else if (key == Key.Left)
{
key = Key.Right;
}
}
switch (key)
{
case Key.Tab:
if (role == MenuItemRole.SubmenuHeader && IsSubmenuOpen && CurrentSelection == null)
{
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
{
NavigateToEnd(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
}
else
{
NavigateToStart(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
}
handled = true;
}
break;
case Key.Right:
if ((role == MenuItemRole.SubmenuHeader) && !IsSubmenuOpen)
{
OpenSubmenuWithKeyboard();
handled = true;
}
break;
case Key.Enter:
{
if (((role == MenuItemRole.SubmenuItem) || (role == MenuItemRole.TopLevelItem)))
{
Debug.Assert(IsHighlighted, "MenuItem got Key.Enter but was not highlighted -- focus did not follow highlight?");
ClickItem(e.UserInitiated);
handled = true;
}
else if (role == MenuItemRole.TopLevelHeader)
{
//
OpenSubmenuWithKeyboard();
handled = true;
}
else if (role == MenuItemRole.SubmenuHeader && !IsSubmenuOpen)
{
OpenSubmenuWithKeyboard();
handled = true;
}
}
break;
// If a menuitem gets a down or up key and the submenu is open, we should focus the first or last
// item in the submenu (respectively). If the submenu is not opened, this will be handled by Menu.
case Key.Down:
{
if (role == MenuItemRole.SubmenuHeader && IsSubmenuOpen && CurrentSelection == null)
{
NavigateToStart(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
handled = true;
}
}
break;
case Key.Up:
{
if (role == MenuItemRole.SubmenuHeader && IsSubmenuOpen && CurrentSelection == null)
{
NavigateToEnd(new ItemNavigateArgs(e.Device, Keyboard.Modifiers));
handled = true;
}
}
break;
case Key.Left:
case Key.Escape:
{
// If Left or Escape is pressed on a Submenu Item or Header, the submenu should be closed.
// Closing the submenu will move focus out of the submenu and onto the parent MenuItem.
if ((role != MenuItemRole.TopLevelHeader) && (role != MenuItemRole.TopLevelItem))
{
if (IsSubmenuOpen)
{
IsSubmenuOpen = false;
handled = true;
}
}
}
break;
}
if (!handled)
{
// Use the unadulterated e.Key here because the later translation
// to FocusNavigationDirection takes this into account.
handled = MenuItemNavigate(e.Key, e.KeyboardDevice.Modifiers);
}
if (handled)
{
e.Handled = true;
}
}
///
/// The Access key for this control was invoked.
///
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
[UIPermissionAttribute(SecurityAction.InheritanceDemand, Unrestricted = true)]
[UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
protected override void OnAccessKey(AccessKeyEventArgs e)
{
base.OnAccessKey(e);
if (!e.IsMultiple)
{
MenuItemRole type = Role;
switch (type)
{
case MenuItemRole.TopLevelItem:
case MenuItemRole.SubmenuItem:
{
ClickItem(e.UserInitiated);
}
break;
case MenuItemRole.TopLevelHeader :
case MenuItemRole.SubmenuHeader :
{
OpenSubmenuWithKeyboard();
}
break;
}
}
}
#pragma warning restore 0688
///
/// This method is invoked when the Items property changes.
///
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
// We use visual triggers to place the popup based on RoleProperty.
// Update the RoleProperty when Items property changes so popup can be placed accordingly.
UpdateRole();
base.OnItemsChanged(e);
}
///
/// Return true if the item is (or is eligible to be) its own ItemUI
///
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is MenuItem) || (item is Separator);
}
///
/// Determine whether the ItemContainerStyle/StyleSelector should apply to the item or not
///
protected override bool ShouldApplyItemContainerStyle(DependencyObject container, object item)
{
if (item is Separator)
{
return false;
}
else
{
return base.ShouldApplyItemContainerStyle(container, item);
}
}
/// Create or identify the element used to display the given item.
protected override DependencyObject GetContainerForItemOverride()
{
return new MenuItem();
}
///
/// Called when the parent of the Visual has changed.
///
/// Old parent or null if the Visual did not have a parent before.
protected internal override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
UpdateRole();
// Windows OS bug:1988393; DevDiv bug:107459
// MenuItem template contains ItemsPresenter where Grid.IsSharedSizeScope="true" and need to inherits PrivateSharedSizeScopeProperty value
// Property inheritance walk the locial tree if possible and skip the visual tree where ItemsPresenter is
// Workaround here will be to copy the property value from MenuItem visual parent
DependencyObject newParent = VisualTreeHelper.GetParentInternal(this);
// logical parent != null
// visual parent != null
// logical parent != visual parent <-- we are in the MenuItem is a logical child of a MenuItem case, not a data container case
// --- Set one-way binding with visual parent for DefinitionBase.PrivateSharedSizeScopeProperty
// NOTE: It seems impossible to get shared size scope to work in this hierarchical scenario
// under normal conditions, so putting this binding here without respecting an author's desire for
// shared size scope on the MenuItem container should be OK, since they wouldn't be able to
// get it to work anyway.
if (Parent != null && newParent != null && Parent != newParent)
{
Binding binding = new Binding();
binding.Path = new PropertyPath(DefinitionBase.PrivateSharedSizeScopeProperty);
binding.Mode = BindingMode.OneWay;
binding.Source = newParent;
BindingOperations.SetBinding(this, DefinitionBase.PrivateSharedSizeScopeProperty, binding);
}
// visual parent == null
// --- Clear binding for DefinitionBase.PrivateSharedSizeScopeProperty
if (newParent == null)
{
BindingOperations.ClearBinding(this, DefinitionBase.PrivateSharedSizeScopeProperty);
}
}
///
/// Called when the Template's tree has been generated
///
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (_submenuPopup != null)
{
_submenuPopup.Closed -= OnPopupClosed;
}
_submenuPopup = GetTemplateChild(PopupTemplateName) as Popup;
if (_submenuPopup != null)
{
_submenuPopup.Closed += OnPopupClosed;
}
}
#endregion
//-------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------
#region Private Methods
private void SetMenuMode(bool menuMode)
{
Debug.Assert(Role == MenuItemRole.TopLevelHeader || Role == MenuItemRole.TopLevelItem, "MenuItem was not top-level");
MenuBase parentMenu = LogicalParent as MenuBase;
if (parentMenu != null)
{
if (parentMenu.IsMenuMode != menuMode)
{
parentMenu.IsMenuMode = menuMode;
}
}
}
///
/// Returns true if the parent has capture. Does not work for submenu items/headers.
///
private bool IsInMenuMode
{
get
{
MenuBase parentMenu = LogicalParent as MenuBase;
if (parentMenu != null)
{
return parentMenu.IsMenuMode;
}
return false;
}
}
///
/// Returns true if the top level header should open when the mouse enters it
///
private bool OpenOnMouseEnter
{
get
{
MenuBase parentMenu = LogicalParent as MenuBase;
if (parentMenu != null)
{
Debug.Assert(!parentMenu.OpenOnMouseEnter || parentMenu.IsMenuMode, "OpenOnMouseEnter can only be true when IsMenuMode is true");
return parentMenu.OpenOnMouseEnter;
}
return false;
}
}
// This is so that MenuItems inside a ContextMenu can behave differently
internal static readonly DependencyProperty InsideContextMenuProperty
= DependencyProperty.RegisterAttached("InsideContextMenu", typeof(bool), typeof(MenuItem),
new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
private bool InsideContextMenu
{
get
{
return (bool)GetValue(InsideContextMenuProperty);
}
}
internal static void SetInsideContextMenuProperty(UIElement element, bool value)
{
element.SetValue(InsideContextMenuProperty, BooleanBoxes.Box(value));
}
///
/// Critical - Calls ClickItem, setting the userInitiated
/// bit, which is used for security purposes later.
/// TreatAsSafe - passes false.
///
[SecurityCritical, SecurityTreatAsSafe]
internal void ClickItem()
{
ClickItem(false);
}
///
/// Critical - Calls OnClickCore, setting the userInitiated
/// bit, which is used for security purposes later.
///
[SecurityCritical]
private void ClickItem(bool userInitiated)
{
try
{
OnClickCore(userInitiated);
}
finally
{
// When you click a top-level item, we need to exit menu mode.
if (Role == MenuItemRole.TopLevelItem)
{
SetMenuMode(false);
}
}
}
internal void ClickHeader()
{
if (!IsKeyboardFocusWithin)
{
FocusOrSelect();
}
if (IsSubmenuOpen)
{
if (Role == MenuItemRole.TopLevelHeader)
{
SetMenuMode(false);
}
}
else
{
// Immediately open the menu when it's clicked. This will stop any
// timers to open or close the submenu.
OpenMenu();
}
}
internal bool OpenMenu()
{
if (!IsSubmenuOpen)
{
// Verify that the parent of the MenuItem is valid;
ItemsControl owner = ItemsControl.ItemsControlFromItemContainer(this);
if (owner == null)
{
owner = VisualTreeHelper.GetParent(this) as ItemsControl;
}
if ((owner != null) && ((owner is MenuItem) || (owner is MenuBase)))
{
// Parent must be MenuItem or MenuBase in order for menus to open.
// Otherwise, odd behavior will occur.
IsSubmenuOpen = true;
return true; // The value was actually changed
}
}
return false;
}
///
/// Set IsSubmenuOpen = true and select the first item.
///
internal void OpenSubmenuWithKeyboard()
{
SetBoolField(BoolField.OpenedWithKeyboard, true);
if (OpenMenu())
{
NavigateToStart(new ItemNavigateArgs(Keyboard.PrimaryDevice, Keyboard.Modifiers));
}
}
///
/// Navigate from one MenuItem to a sibling.
///
/// Raw key that was pressed (RTL is respected within this method).
///
/// true if navigation was successful.
private bool MenuItemNavigate(Key key, ModifierKeys modifiers)
{
if (key == Key.Left || key == Key.Right || key == Key.Up || key == Key.Down)
{
ItemsControl parent = ItemsControlFromItemContainer(this);
if (parent != null)
{
if (!parent.HasItems)
{
return false;
}
int count = parent.Items.Count;
// Optimize for the case where the submenu contains one item.
if (count == 1 && !(parent is Menu))
{
// Return true if we were navigating up/down (we cycled around).
if (key == Key.Up && key == Key.Down)
{
return true;
}
}
object previousFocus = Keyboard.FocusedElement;
parent.NavigateByLine(KeyboardNavigation.KeyToTraversalDirection(key), new ItemNavigateArgs(Keyboard.PrimaryDevice, modifiers));
object currentFocus = Keyboard.FocusedElement;
if ((currentFocus != previousFocus) && (currentFocus != this))
{
return true;
}
}
}
return false;
}
///
/// Returns logical parent; either Parent or ItemsControlFromItemContainer(this).
///
///
internal object LogicalParent
{
get
{
if (Parent != null)
{
return Parent;
}
return ItemsControlFromItemContainer(this);
}
}
///
/// Return the current sibling of this MenuItem -- the
/// CurrentSelection of the parent as long as it isn't us.
///
private MenuItem CurrentSibling
{
get
{
object parent = LogicalParent;
MenuItem menuItemParent = parent as MenuItem;
MenuItem sibling = null;
if (menuItemParent != null)
{
sibling = menuItemParent.CurrentSelection;
}
else
{
MenuBase menuParent = parent as MenuBase;
if (menuParent != null)
{
sibling = menuParent.CurrentSelection;
}
}
if (sibling == this)
{
sibling = null;
}
return sibling;
}
}
///
/// Returns true if the mouse is somewhere in the hierarchy
/// but not over this node. Note that this is slightly different
/// from CurrentSibling.IsMouseOver because there are regions in
/// the menu which are not occupied by siblings and we're interested
/// in that case too.
///
private bool IsMouseOverSibling
{
get
{
FrameworkElement parent = LogicalParent as FrameworkElement;
// If the mouse is over our parent but not over us, then
// the mouse must be somewhere in a sibling hierarchy.
//
// NOTE: If this check were changed to CurrentSibling.IsMouseOver
// then our behavior becomes identical to the behavior
// of the start menu, where a menu doesn't close unless
// you have settled on another hierarchy. Here we will
// close unless you are settled on this item's hierarchy.
if (parent != null && IsMouseReallyOver(parent) && !IsMouseOver)
{
return true;
}
return false;
}
}
///
/// Performs an IsMouseOver test but accounts for elements that have capture
/// and instead checks their children.
///
/// The element to test.
/// True if the mouse is over the element, regardless of capture. False otherwise.
private static bool IsMouseReallyOver(FrameworkElement elem)
{
bool isMouseOver = elem.IsMouseOver;
if (isMouseOver)
{
if ((Mouse.Captured == elem) && (Mouse.DirectlyOver == elem))
{
// The mouse is not over any of the children of this captured element.
// Assuming that this means that the mouse is not really over the element.
return false;
}
}
return isMouseOver;
}
///
/// Select this item and expand the hierarchy below it.
///
///
private void OpenHierarchy(MenuItemRole role)
{
FocusOrSelect();
if (role == MenuItemRole.TopLevelHeader || role == MenuItemRole.SubmenuHeader)
{
OpenMenu();
}
}
///
/// Focus this item or, if that fails, just mark it selected.
///
private void FocusOrSelect()
{
// Setting focus will cause the item to be selected,
// but if we fail to focus we should still select.
// (This is to help enable focusless menus).
// Check IsKeyboardFocusWithin to allow rich content within the menuitem.
if (!IsKeyboardFocusWithin)
{
Focus();
}
if (!IsSelected)
{
// If it's already focused, make sure it's also selected.
IsSelected = true;
}
// If the item is selected we should ensure that it's highlighted.
if (IsSelected && !IsHighlighted)
{
IsHighlighted = true;
}
}
private void SetTimerToOpenHierarchy()
{
if (_openHierarchyTimer == null)
{
_openHierarchyTimer = new DispatcherTimer(DispatcherPriority.Normal);
_openHierarchyTimer.Tick += (EventHandler)delegate(object sender, EventArgs e)
{
OpenHierarchy(Role);
StopTimer(ref _openHierarchyTimer);
};
}
else
{
_openHierarchyTimer.Stop();
}
StartTimer(_openHierarchyTimer);
}
private void SetTimerToCloseHierarchy()
{
if (_closeHierarchyTimer == null)
{
_closeHierarchyTimer = new DispatcherTimer(DispatcherPriority.Normal);
_closeHierarchyTimer.Tick += (EventHandler)delegate(object sender, EventArgs e)
{
// Deselect the item; will remove highlight and collapse hierarchy.
IsSelected = false;
StopTimer(ref _closeHierarchyTimer);
};
}
else
{
_closeHierarchyTimer.Stop();
}
StartTimer(_closeHierarchyTimer);
}
private void StopTimer(ref DispatcherTimer timer)
{
if (timer != null)
{
timer.Stop();
timer = null;
}
}
private void StartTimer(DispatcherTimer timer)
{
Debug.Assert(timer != null, "timer should not be null.");
Debug.Assert(!timer.IsEnabled, "timer should not be running.");
timer.Interval = TimeSpan.FromMilliseconds(SystemParameters.MenuShowDelay);
timer.Start();
}
private static object OnCoerceAcceleratorKey(DependencyObject d, object value)
{
if (value == null)
{
string inputGestureText = ((MenuItem)d).InputGestureText;
if (inputGestureText != String.Empty)
{
value = inputGestureText;
}
}
return value;
}
#endregion
//-------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------
#region Private Fields
///
/// Tracks the current selection in the items collection (i.e. submenu)
/// of this MenuItem.
///
private MenuItem CurrentSelection
{
get
{
return _currentSelection;
}
set
{
if (_currentSelection != null)
{
_currentSelection.IsSelected = false;
}
_currentSelection = value;
if (_currentSelection != null)
{
_currentSelection.IsSelected = true;
}
// NOTE: (Win32 disparity) If CurrentSelection changes to null
// and the focus was within the old CurrentSelection, we
// the parent should take focus back. In Win32 the "virtual"
// focus was tracked by way of the currently selected guy in
// If you were selected but none of your children were, you
// were effectively selected. It should be relatively easy to
// enable this behavior by checking if IsKeyboardFocusWithin is true
// on the previous child and then setting Focus to ourselves
// when _currentSelection becomes null. We would need to do this
// here and in MenuBase.CurrentSelection.
}
}
private bool GetBoolField(BoolField field)
{
return (_menuItemBoolFieldStore & field) != 0;
}
private void SetBoolField(BoolField field, bool value)
{
if (value)
{
_menuItemBoolFieldStore |= field;
}
else
{
_menuItemBoolFieldStore &= (~field);
}
}
[Flags]
private enum BoolField
{
OpenedWithKeyboard = 0x01,
IgnoreNextMouseLeave = 0x02,
IgnoreMouseEvents = 0x04,
MouseEnterOnMouseMove = 0x08,
CanExecuteInvalid = 0x10,
}
//
// This property
// 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject
// 2. This is a performance optimization
//
internal override int EffectiveValuesInitialSize
{
get { return 42; }
}
private bool CanExecute
{
get { return !ReadControlFlag(ControlBoolFlags.CommandDisabled); }
set
{
if (value != CanExecute)
{
WriteControlFlag(ControlBoolFlags.CommandDisabled, !value);
CoerceValue(IsEnabledProperty);
}
}
}
private const string PopupTemplateName = "PART_Popup";
private BoolField _menuItemBoolFieldStore;
private MenuItem _currentSelection;
private Popup _submenuPopup;
DispatcherTimer _openHierarchyTimer;
DispatcherTimer _closeHierarchyTimer;
// This is to hold onto a CanExecuteChanged event handler to respond to changes in a command's CanExecute property
private static readonly UncommonField CanExecuteChangedHandler = new UncommonField();
///
/// Critical: Setting this to true indicates that the mouse down was user initiated
///
[SecurityCritical]
private bool _userInitiatedPress;
#endregion
#region DTypeThemeStyleKey
// Returns the DependencyObjectType for the registered ThemeStyleKey's default
// value. Controls will override this method to return approriate types.
internal override DependencyObjectType DTypeThemeStyleKey
{
get { return _dType; }
}
private static DependencyObjectType _dType;
#endregion DTypeThemeStyleKey
#region ItemsStyleKey
///
/// Resource Key for the SeparatorStyle
///
public static ResourceKey SeparatorStyleKey
{
get
{
if (_cacheSeparatorStyle == null)
{
_cacheSeparatorStyle = new SystemThemeKey(SystemResourceKeyID.MenuItemSeparatorStyle);
}
return _cacheSeparatorStyle;
}
}
private static SystemThemeKey _cacheSeparatorStyle;
#endregion ItemsStyleKey
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XamlPointCollectionSerializer.cs
- DataObject.cs
- TCEAdapterGenerator.cs
- HeaderCollection.cs
- TransformerInfoCollection.cs
- FixedSOMTableCell.cs
- _emptywebproxy.cs
- PluralizationService.cs
- MissingMethodException.cs
- WebAdminConfigurationHelper.cs
- SerialStream.cs
- SymbolPair.cs
- CompilerInfo.cs
- DPTypeDescriptorContext.cs
- ImageMapEventArgs.cs
- FactoryGenerator.cs
- SoapSchemaMember.cs
- UrlRoutingModule.cs
- NonSerializedAttribute.cs
- TableCell.cs
- OneOfTypeConst.cs
- LinearGradientBrush.cs
- SimpleRecyclingCache.cs
- TableCell.cs
- ExceptionWrapper.cs
- _ScatterGatherBuffers.cs
- XPathNavigatorKeyComparer.cs
- Transaction.cs
- ValidatorCollection.cs
- MultipartIdentifier.cs
- SponsorHelper.cs
- TransactionManager.cs
- ITreeGenerator.cs
- TranslateTransform3D.cs
- CodeSnippetExpression.cs
- glyphs.cs
- PenLineJoinValidation.cs
- figurelength.cs
- ObjectPropertyMapping.cs
- DatePickerAutomationPeer.cs
- AppModelKnownContentFactory.cs
- AutoGeneratedFieldProperties.cs
- MgmtConfigurationRecord.cs
- AdornedElementPlaceholder.cs
- regiisutil.cs
- Geometry.cs
- StringWriter.cs
- HtmlShimManager.cs
- RevocationPoint.cs
- ExceptionNotification.cs
- WorkflowRuntimeEndpoint.cs
- CatalogUtil.cs
- FormCollection.cs
- ActiveXHelper.cs
- _SafeNetHandles.cs
- Html32TextWriter.cs
- securitycriticaldataformultiplegetandset.cs
- SqlInternalConnectionSmi.cs
- SystemColors.cs
- XmlSortKeyAccumulator.cs
- PathTooLongException.cs
- CacheRequest.cs
- SecondaryViewProvider.cs
- Vector3DAnimationUsingKeyFrames.cs
- PropertyChangingEventArgs.cs
- XmlQualifiedName.cs
- SrgsRuleRef.cs
- SqlUserDefinedTypeAttribute.cs
- ToolBarButtonClickEvent.cs
- RetrieveVirtualItemEventArgs.cs
- CipherData.cs
- CharacterBufferReference.cs
- IConvertible.cs
- SqlPersistenceWorkflowInstanceDescription.cs
- COM2ExtendedTypeConverter.cs
- OpacityConverter.cs
- FontCacheUtil.cs
- XmlUnspecifiedAttribute.cs
- PlainXmlWriter.cs
- ProfileSettings.cs
- precedingsibling.cs
- FontEditor.cs
- CatalogPartCollection.cs
- StreamMarshaler.cs
- NavigationEventArgs.cs
- ExecutedRoutedEventArgs.cs
- Window.cs
- WebCategoryAttribute.cs
- CodeExpressionStatement.cs
- PenLineJoinValidation.cs
- SHA512.cs
- PixelShader.cs
- DataReceivedEventArgs.cs
- DBBindings.cs
- ListDictionary.cs
- AttributeSetAction.cs
- documentsequencetextcontainer.cs
- Utility.cs
- ColumnCollectionEditor.cs
- XmlWriterSettings.cs