Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Controls / Primitives / MenuBase.cs / 1305600 / MenuBase.cs
//---------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All rights reserved. // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.KnownBoxes; using MS.Utility; using System.ComponentModel; using System.Collections; using System.Collections.Specialized; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Diagnostics; using System.Windows.Threading; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Input; using System.Security; using System.Security.Permissions; using System; namespace System.Windows.Controls.Primitives { ////// Control that defines a menu of choices for users to invoke. /// [Localizability(LocalizationCategory.Menu)] [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(MenuItem))] public abstract class MenuBase : ItemsControl { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Default DependencyObject constructor /// ////// Automatic determination of current Dispatcher. Use alternative constructor /// that accepts a Dispatcher for best performance. /// protected MenuBase() : base() { } static MenuBase() { EventManager.RegisterClassHandler(typeof(MenuBase), MenuItem.PreviewClickEvent, new RoutedEventHandler(OnMenuItemPreviewClick)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMouseButtonDown)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseUpEvent, new MouseButtonEventHandler(OnMouseButtonUp)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture)); EventManager.RegisterClassHandler(typeof(MenuBase), MenuBase.IsSelectedChangedEvent, new RoutedPropertyChangedEventHandler(OnIsSelectedChanged)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnPromotedMouseButton)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseUpEvent, new MouseButtonEventHandler(OnPromotedMouseButton)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.PreviewMouseUpOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk)); EventManager.RegisterClassHandler(typeof(MenuBase), Keyboard.PreviewKeyboardInputProviderAcquireFocusEvent, new KeyboardInputProviderAcquireFocusEventHandler(OnPreviewKeyboardInputProviderAcquireFocus), true); EventManager.RegisterClassHandler(typeof(MenuBase), Keyboard.KeyboardInputProviderAcquireFocusEvent, new KeyboardInputProviderAcquireFocusEventHandler(OnKeyboardInputProviderAcquireFocus), true); FocusManager.IsFocusScopeProperty.OverrideMetadata(typeof(MenuBase), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); // 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(MenuBase), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits)); } #endregion //-------------------------------------------------------------------- // // Protected Methods // //------------------------------------------------------------------- #region Protected Methods /// /// Called when any mouse button is pressed on this subtree /// /// Sender of the event. /// Event arguments. private static void OnMouseButtonDown(object sender, MouseButtonEventArgs e) { ((MenuBase)sender).HandleMouseButton(e); } ////// Called when any mouse right button is released on this subtree /// /// Sender of the event. /// Event arguments. private static void OnMouseButtonUp(object sender, MouseButtonEventArgs e) { ((MenuBase)sender).HandleMouseButton(e); } ////// Called when any mouse button is pressed or released on this subtree /// /// Event arguments. protected virtual void HandleMouseButton(MouseButtonEventArgs e) { } private static void OnClickThroughThunk(object sender, MouseButtonEventArgs e) { ((MenuBase)sender).OnClickThrough(e); } private void OnClickThrough(MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Right) { if (HasCapture) { bool close = true; if (e.ButtonState == MouseButtonState.Released) { // Check to see if we should ignore the this mouse release if (e.ChangedButton == MouseButton.Left && IgnoreNextLeftRelease) { IgnoreNextLeftRelease = false; close = false; // don't close } else if (e.ChangedButton == MouseButton.Right && IgnoreNextRightRelease) { IgnoreNextRightRelease = false; close = false; // don't close } } if (close) { IsMenuMode = false; } } } } // This is called on MouseLeftButtonDown, MouseLeftButtonUp, MouseRightButtonDown, MouseRightButtonUp private static void OnPromotedMouseButton(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) { // If it wasn't outside the subtree, we should handle the mouse event. // This makes things consistent so that just in case one of our children // didn't handle the event, it doesn't escape the menu hierarchy. e.Handled = true; } } ////// Called when IsMouseOver changes on this element. /// /// protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); // if we don't have capture and the mouse left (but the item isn't selected), then we shouldn't have anything selected. if (!HasCapture && !IsMouseOver && CurrentSelection != null && !CurrentSelection.IsKeyboardFocused && !CurrentSelection.IsSubmenuOpen) { CurrentSelection = null; } } // This method ensures that whenever focus is given to an element // within a menu, that the menu enters menu mode. This can't be // done with a simple IsFocusWithin changed handler because we // need to actually enter menu mode before focus changes. private static void OnPreviewKeyboardInputProviderAcquireFocus(object sender, KeyboardInputProviderAcquireFocusEventArgs e) { MenuBase menu = (MenuBase) sender; // If we haven't already pushed menu mode, we need to do it before // focus enters the menu for the first time if (!menu.IsKeyboardFocusWithin && !menu.HasPushedMenuMode) { // Call PushMenuMode just before focus enters the menu... menu.PushMenuMode(/*isAcquireFocusMenuMode*/ true); } } // This method ensures that whenever focus is not acquired // but MenuMode has been pushed with the expection, a // corresponding PopMenu is performed. private static void OnKeyboardInputProviderAcquireFocus(object sender, KeyboardInputProviderAcquireFocusEventArgs e) { MenuBase menu = (MenuBase) sender; if (!menu.IsKeyboardFocusWithin && !e.FocusAcquired && menu.IsAcquireFocusMenuMode) { Debug.Assert(menu.HasPushedMenuMode); // The input provider did not acquire focus. So we will not // succeed in setting focus to the desired element within the // menu. menu.PopMenuMode(); } } ////// Called when the focus is no longer on or within this element. /// /// protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e) { base.OnIsKeyboardFocusWithinChanged(e); if (IsKeyboardFocusWithin) { // When focus enters the menu, we should enter menu mode. if (!IsMenuMode) { IsMenuMode = true; OpenOnMouseEnter = false; } if (KeyboardNavigation.IsKeyboardMostRecentInputDevice()) { // Turn on keyboard cues b/c we took focus with the keyboard KeyboardNavigation.EnableKeyboardCues(this, true); } } else { // Turn off keyboard cues KeyboardNavigation.EnableKeyboardCues(this, false); if (IsMenuMode) { // When showing a ContextMenu of a MenuItem, the ContextMenu will take focus // out of this menu's subtree. The ContextMenu takes capture before taking // focus, so if we are in MenuMode but don't have capture then we are waiting // for the context menu to close. Thus, we should only exit menu mode when // we have capture. if (HasCapture) { IsMenuMode = false; } } else { // Okay, we weren't in menu mode but we could have had a selection (mouse hovering), so clear that if (CurrentSelection != null) { CurrentSelection = null; } } } InvokeMenuOpenedClosedAutomationEvent(IsKeyboardFocusWithin); } private void InvokeMenuOpenedClosedAutomationEvent(bool open) { AutomationEvents automationEvent = open ? AutomationEvents.MenuOpened : AutomationEvents.MenuClosed; if (AutomationPeer.ListenerExists(automationEvent)) { AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this); if (peer != null) { if (open) { // We raise the event async to allow PopupRoot to hookup Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param) { peer.RaiseAutomationEvent(automationEvent); return null; }), null); } else { peer.RaiseAutomationEvent(automationEvent); } } } } internal static readonly RoutedEvent IsSelectedChangedEvent = EventManager.RegisterRoutedEvent( "IsSelectedChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(MenuBase)); private static void OnIsSelectedChanged(object sender, RoutedPropertyChangedEventArgs e) { // We assume that within a menu the only top-level menu items are direct children of // the one and only top-level menu. MenuItem newSelectedMenuItem = e.OriginalSource as MenuItem; if (newSelectedMenuItem != null) { MenuBase menu = (MenuBase)sender; // If the selected item is a child of ours, make it the current selection. // If the selection changes from a top-level menu item with its submenu // open to another, the new selection's submenu should be open. if (e.NewValue) { if ((menu.CurrentSelection != newSelectedMenuItem) && (newSelectedMenuItem.LogicalParent == menu)) { bool wasSubmenuOpen = false; if (menu.CurrentSelection != null) { wasSubmenuOpen = menu.CurrentSelection.IsSubmenuOpen; menu.CurrentSelection.SetCurrentValueInternal(MenuItem.IsSubmenuOpenProperty, BooleanBoxes.FalseBox); } menu.CurrentSelection = newSelectedMenuItem; if (menu.CurrentSelection != null && wasSubmenuOpen) { // Only open the submenu if it's a header (i.e. has items) MenuItemRole role = menu.CurrentSelection.Role; if (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.TopLevelHeader) { if (menu.CurrentSelection.IsSubmenuOpen != wasSubmenuOpen) { menu.CurrentSelection.SetCurrentValueInternal(MenuItem.IsSubmenuOpenProperty, BooleanBoxes.Box(wasSubmenuOpen)); } } } } } else { // As in MenuItem.OnIsSelectedChanged, if the item is deselected // and it's our current selection, set CurrentSelection to null. if (menu.CurrentSelection == newSelectedMenuItem) { menu.CurrentSelection = null; } } e.Handled = true; } } private bool IsDescendant(DependencyObject node) { return IsDescendant(this, node); } internal static bool IsDescendant(DependencyObject reference, DependencyObject node) { bool success = false; DependencyObject curr = node; while (curr != null) { if (curr == reference) { success = true; break; } // Find popup if curr is a PopupRoot PopupRoot popupRoot = curr as PopupRoot; if (popupRoot != null) { //Now Popup does not have a visual link to its parent (for context menu) //it is stored in its parent's arraylist (DP) //so we get its parent by looking at PlacementTarget Popup popup = popupRoot.Parent as Popup; curr = popup; if (popup != null) { // Try the poup Parent curr = popup.Parent; // Otherwise fall back to placement target if (curr == null) { curr = popup.PlacementTarget; } } } else // Otherwise walk tree { curr = PopupControlService.FindParent(curr); } } return success; } /// /// This is the method that responds to the KeyDown event. /// /// Event arguments protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); Key key = e.Key; switch (key) { case Key.Escape: { if (CurrentSelection != null && CurrentSelection.IsSubmenuOpen) { CurrentSelection.SetCurrentValueInternal(MenuItem.IsSubmenuOpenProperty, BooleanBoxes.FalseBox); OpenOnMouseEnter = false; e.Handled = true; } else { KeyboardLeaveMenuMode(); e.Handled = true; } } break; case Key.System: if ((e.SystemKey == Key.LeftAlt) || (e.SystemKey == Key.RightAlt) || (e.SystemKey == Key.F10)) { KeyboardLeaveMenuMode(); e.Handled = true; } break; } } ////// 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); } ///Create or identify the element used to display the given item. protected override DependencyObject GetContainerForItemOverride() { return new MenuItem(); } #endregion //-------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------- #region Private Methods ////// Called when this element loses capture. /// private static void OnLostMouseCapture(object sender, MouseEventArgs e) { MenuBase menu = sender as MenuBase; // // Use the same technique employed in ComoboBox.OnLostMouseCapture to allow another control in the // application to temporarily take capture and then take it back afterwards. if (Mouse.Captured != menu) { if (e.OriginalSource == menu) { // If capture is null or it's not below the menu, close. // More workaround for task 22022 -- check if it's a descendant (following Logical links too) if (Mouse.Captured == null || !MenuBase.IsDescendant(menu, Mouse.Captured as DependencyObject)) { menu.IsMenuMode = false; } } else { if (MenuBase.IsDescendant(menu, e.OriginalSource as DependencyObject)) { // Take capture if one of our children gave up capture if (menu.IsMenuMode && Mouse.Captured == null && MS.Win32.SafeNativeMethods.GetCapture() == IntPtr.Zero) { Mouse.Capture(menu, CaptureMode.SubTree); e.Handled = true; } } else { menu.IsMenuMode = false; } } } } ////// Called when any menu item within this subtree got clicked. /// Closes all submenus in this tree. /// /// /// private static void OnMenuItemPreviewClick(object sender, RoutedEventArgs e) { MenuBase menu = ((MenuBase)sender); MenuItem menuItemSource = e.OriginalSource as MenuItem; if ((menuItemSource != null) && !menuItemSource.StaysOpenOnClick) { MenuItemRole role = menuItemSource.Role; if (role == MenuItemRole.TopLevelItem || role == MenuItemRole.SubmenuItem) { menu.IsMenuMode = false; e.Handled = true; } } } ////// Called when IsMenuMode changes. /// internal event EventHandler InternalMenuModeChanged { add { EventHandlersStoreAdd(InternalMenuModeChangedKey, value); } remove { EventHandlersStoreRemove(InternalMenuModeChangedKey, value); } } private static readonly EventPrivateKey InternalMenuModeChangedKey = new EventPrivateKey(); ////// Critical - Accesses HwndSource /// TreatAsSafe - No information exposed. Simply decides whether or not to restore focus. /// [SecurityCritical, SecurityTreatAsSafe] private void RestorePreviousFocus() { // Only restore focus if focus is still within the menu. If // focus has already been moved outside of the menu, then // we don't want to disturb it. if (IsKeyboardFocusWithin) { // Only restore WPF focus if the HWND with focus is an // HwndSource. This enables child HWNDs, other top-level // non-WPF HWNDs, or even child HWNDs of other WPF top-level // windows to retain focus when menus are dismissed. IntPtr hwndWithFocus = MS.Win32.UnsafeNativeMethods.GetFocus(); HwndSource hwndSourceWithFocus = hwndWithFocus != IntPtr.Zero ? HwndSource.CriticalFromHwnd(hwndWithFocus) : null; if(hwndSourceWithFocus != null) { // We restore focus by setting focus to the parent's focus // scope. This may not seem correct, because it presumes // the focus came from the logical-focus element of the // parent scope. In fact, it could have come from any // number of places. However, we have not figured out a // better solution for restoring focus across scenarios // such as: // // 1) A context menu of a menu item. // 2) Two menus side-by-side // 3) A menu and a toolbar side-by-side // // Simply remembering the last element with focus and // restoring focus to it does not work. For example, // two menus side-by-side will end up remembering each // other, and you can get stuck in an infinite loop. // // Restoring focus through the parent's focus scope will // not directly work if you open one window's menu from // another window. Visual Studio, as an example, will // intercept the focus change events and forward // appropriately for the scenario of restoring focus to // an element in a different top-level window. // DependencyObject parent = Parent; // if (parent == null) // { // If there is no logical parent, use the visual parent. // parent = VisualTreeHelper.GetParent(this); // } // if (parent != null) // { // IInputElement parentScope = FocusManager.GetFocusScope(parent) as IInputElement; // if (parentScope != null) // { // Keyboard.Focus(parentScope); // } // } // Unfortunately setting focus to the parent focusscope tripped up VS in the scenario where // Menus are contained within ToolBars. In this case when the Menu is dismissed they want // focus to be restored to the element in the main window that previously had focus. However // since ToolBar is the parent focusscope for the Menu we end up restoring focus to its // focusedelment. It is also noted that this implementation is a behavioral change from .Net 3.5. // Hence we are putting back the old behavior which is to set Keyboard.Focus to null which will // delegate focus through the main window to its focusedelement. Keyboard.Focus(null); } else { // In the case where Win32 focus is not on a WPF // HwndSource, we just clear WPF focus completely. // // Note that calling Focus(null) will set focus to the root // element of the active source, which is not what we want. Keyboard.ClearFocus(); } } } // From all of our children, set the InMenuMode property // If turning this property off, recurse to all submenus internal static void SetSuspendingPopupAnimation(ItemsControl menu, MenuItem ignore, bool suspend) { // menu can be either a MenuBase or MenuItem if (menu != null) { int itemsCount = menu.Items.Count; for (int i = 0; i < itemsCount; i++) { MenuItem mi = menu.ItemContainerGenerator.ContainerFromIndex(i) as MenuItem; if (mi != null && mi != ignore && mi.IsSuspendingPopupAnimation != suspend) { mi.IsSuspendingPopupAnimation = suspend; // If leaving menu mode, clear property on all // submenus of this menu if (!suspend) { SetSuspendingPopupAnimation(mi, null, suspend); } } } } } internal void KeyboardLeaveMenuMode() { // If we're in MenuMode, exit. This will relinquish capture, // clear CurrentSelection, and RestorePreviousFocus if (IsMenuMode) { IsMenuMode = false; } else { // CurrentSelection = null; RestorePreviousFocus(); } } #endregion //------------------------------------------------------------------- // // Private Fields // //-------------------------------------------------------------------- #region Private Fields ////// Currently selected item in this menu or submenu. /// ///internal MenuItem CurrentSelection { get { return _currentSelection; } set { // Even if we don't have capture we should move focus when one item is already focused. bool wasFocused = false; if (_currentSelection != null) { wasFocused = _currentSelection.IsKeyboardFocused; _currentSelection.SetCurrentValueInternal(MenuItem.IsSelectedProperty, BooleanBoxes.FalseBox); } _currentSelection = value; if (_currentSelection != null) { _currentSelection.SetCurrentValueInternal(MenuItem.IsSelectedProperty, BooleanBoxes.TrueBox); if (wasFocused) { _currentSelection.Focus(); } } } } internal bool HasCapture { get { return Mouse.Captured == this; } } internal bool IgnoreNextLeftRelease { get { return _bitFlags[(int)MenuBaseFlags.IgnoreNextLeftRelease]; } set { _bitFlags[(int)MenuBaseFlags.IgnoreNextLeftRelease] = value; } } internal bool IgnoreNextRightRelease { get { return _bitFlags[(int)MenuBaseFlags.IgnoreNextRightRelease]; } set { _bitFlags[(int)MenuBaseFlags.IgnoreNextRightRelease] = value; } } internal bool IsMenuMode { get { return _bitFlags[(int)MenuBaseFlags.IsMenuMode]; } set { Debug.Assert(CheckAccess(), "IsMenuMode requires context access"); bool isMenuMode = _bitFlags[(int)MenuBaseFlags.IsMenuMode]; if (isMenuMode != value) { isMenuMode = _bitFlags[(int)MenuBaseFlags.IsMenuMode] = value; if (isMenuMode) { // Take capture so that all mouse messages stay below the menu. if (!IsDescendant(this, Mouse.Captured as Visual) && !Mouse.Capture(this, CaptureMode.SubTree)) { // If we're unable to take capture, leave menu mode immediately. isMenuMode = _bitFlags[(int)MenuBaseFlags.IsMenuMode] = false; } else { // If we haven't pushed the menu mode yet (which // should have already happened if keyboard focus // is set within the menu), push it now. if (!HasPushedMenuMode) { PushMenuMode(/*isAcquireFocusMenuMode*/ false); } RaiseClrEvent(InternalMenuModeChangedKey, EventArgs.Empty); } } if (!isMenuMode) { bool wasSubmenuOpen = false; if (CurrentSelection != null) { wasSubmenuOpen = CurrentSelection.IsSubmenuOpen; CurrentSelection.IsSubmenuOpen = false; CurrentSelection = null; } // Note that this code path is also used to cleanup // the case where setting IsMenuMode=true fails due // to failure to gain capture. We pop out of the menu // mode irrespective of where it was pushed. if (HasPushedMenuMode) { // Call PopMenuMode before we do much else, so that // focus changes will properly activate windows. PopMenuMode(); } if (!value) { // Fire the event before capture is released and after submenus have been closed. RaiseClrEvent(InternalMenuModeChangedKey, EventArgs.Empty); } // Clear suspending animation flags on all descendant menuitems SetSuspendingPopupAnimation(this, null, false); // if (HasCapture) { Mouse.Capture(null); } RestorePreviousFocus(); } // Assume menu items should open when the mouse hovers over them OpenOnMouseEnter = isMenuMode; } } } // This bool is used by top level menu items to // determine if they should open on mouse enter // Menu items shouldn't open if the use hit Alt // to get in menu mode and then hovered over the item internal bool OpenOnMouseEnter { get { return _bitFlags[(int)MenuBaseFlags.OpenOnMouseEnter]; } set { _bitFlags[(int)MenuBaseFlags.OpenOnMouseEnter] = value; } } /// /// Critical - Accesses PresentationSource /// TreatAsSafe - No information exposed. Simply notifies the /// PresentationSource to enter/leave menu mode. /// [SecurityCritical, SecurityTreatAsSafe] private void PushMenuMode(bool isAcquireFocusMenuMode) { Debug.Assert(_pushedMenuMode == null); _pushedMenuMode = PresentationSource.CriticalFromVisual(this); Debug.Assert(_pushedMenuMode != null); IsAcquireFocusMenuMode = isAcquireFocusMenuMode; InputManager.UnsecureCurrent.PushMenuMode(_pushedMenuMode); } ////// Critical - Accesses PresentationSource /// TreatAsSafe - No information exposed. Simply notifies the /// PresentationSource to enter/leave menu mode. /// [SecurityCritical, SecurityTreatAsSafe] private void PopMenuMode() { Debug.Assert(_pushedMenuMode != null); PresentationSource pushedMenuMode = _pushedMenuMode; _pushedMenuMode = null; IsAcquireFocusMenuMode = false; InputManager.UnsecureCurrent.PopMenuMode(pushedMenuMode); } ////// Critical - Accesses PresentationSource /// TreatAsSafe - No information exposed. Simply returns /// whether or not we have pushed menu mode. /// private bool HasPushedMenuMode { [SecurityCritical, SecurityTreatAsSafe] get { return _pushedMenuMode != null; } } ////// This boolean determines if the PushMenuMode was /// performed due to acquire focus or due to programmatic /// set of IsMenuMode. /// private bool IsAcquireFocusMenuMode { get { return _bitFlags[(int)MenuBaseFlags.IsAcquireFocusMenuMode]; } set { _bitFlags[(int)MenuBaseFlags.IsAcquireFocusMenuMode] = value; } } [SecurityCritical] private PresentationSource _pushedMenuMode; private MenuItem _currentSelection; private BitVector32 _bitFlags = new BitVector32(0); private enum MenuBaseFlags { IgnoreNextLeftRelease = 0x01, IgnoreNextRightRelease = 0x02, IsMenuMode = 0x04, OpenOnMouseEnter = 0x08, IsAcquireFocusMenuMode = 0x10, } #endregion // Notes: // We want to enforce: // 1) IsKeyboardFocused -> IsHighlighted // 2) IsKeyboardFocusWithin -> Mouse.Captured is an ancestor of Keyboard.FocusedElement // 3) IsSubmenuOpen -> IsHighlighted and IsKeyboardFocusWithin // these conditions are violated only in the case of mousing from one submenu to another and there is a delay. } } // 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 MS.Internal; using MS.Internal.KnownBoxes; using MS.Utility; using System.ComponentModel; using System.Collections; using System.Collections.Specialized; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Diagnostics; using System.Windows.Threading; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Input; using System.Security; using System.Security.Permissions; using System; namespace System.Windows.Controls.Primitives { ////// Control that defines a menu of choices for users to invoke. /// [Localizability(LocalizationCategory.Menu)] [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(MenuItem))] public abstract class MenuBase : ItemsControl { //------------------------------------------------------------------- // // Constructors // //------------------------------------------------------------------- #region Constructors ////// Default DependencyObject constructor /// ////// Automatic determination of current Dispatcher. Use alternative constructor /// that accepts a Dispatcher for best performance. /// protected MenuBase() : base() { } static MenuBase() { EventManager.RegisterClassHandler(typeof(MenuBase), MenuItem.PreviewClickEvent, new RoutedEventHandler(OnMenuItemPreviewClick)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMouseButtonDown)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseUpEvent, new MouseButtonEventHandler(OnMouseButtonUp)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture)); EventManager.RegisterClassHandler(typeof(MenuBase), MenuBase.IsSelectedChangedEvent, new RoutedPropertyChangedEventHandler(OnIsSelectedChanged)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseDownEvent, new MouseButtonEventHandler(OnPromotedMouseButton)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.MouseUpEvent, new MouseButtonEventHandler(OnPromotedMouseButton)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk)); EventManager.RegisterClassHandler(typeof(MenuBase), Mouse.PreviewMouseUpOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk)); EventManager.RegisterClassHandler(typeof(MenuBase), Keyboard.PreviewKeyboardInputProviderAcquireFocusEvent, new KeyboardInputProviderAcquireFocusEventHandler(OnPreviewKeyboardInputProviderAcquireFocus), true); EventManager.RegisterClassHandler(typeof(MenuBase), Keyboard.KeyboardInputProviderAcquireFocusEvent, new KeyboardInputProviderAcquireFocusEventHandler(OnKeyboardInputProviderAcquireFocus), true); FocusManager.IsFocusScopeProperty.OverrideMetadata(typeof(MenuBase), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox)); // 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(MenuBase), new FrameworkPropertyMetadata(BooleanBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits)); } #endregion //-------------------------------------------------------------------- // // Protected Methods // //------------------------------------------------------------------- #region Protected Methods /// /// Called when any mouse button is pressed on this subtree /// /// Sender of the event. /// Event arguments. private static void OnMouseButtonDown(object sender, MouseButtonEventArgs e) { ((MenuBase)sender).HandleMouseButton(e); } ////// Called when any mouse right button is released on this subtree /// /// Sender of the event. /// Event arguments. private static void OnMouseButtonUp(object sender, MouseButtonEventArgs e) { ((MenuBase)sender).HandleMouseButton(e); } ////// Called when any mouse button is pressed or released on this subtree /// /// Event arguments. protected virtual void HandleMouseButton(MouseButtonEventArgs e) { } private static void OnClickThroughThunk(object sender, MouseButtonEventArgs e) { ((MenuBase)sender).OnClickThrough(e); } private void OnClickThrough(MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Right) { if (HasCapture) { bool close = true; if (e.ButtonState == MouseButtonState.Released) { // Check to see if we should ignore the this mouse release if (e.ChangedButton == MouseButton.Left && IgnoreNextLeftRelease) { IgnoreNextLeftRelease = false; close = false; // don't close } else if (e.ChangedButton == MouseButton.Right && IgnoreNextRightRelease) { IgnoreNextRightRelease = false; close = false; // don't close } } if (close) { IsMenuMode = false; } } } } // This is called on MouseLeftButtonDown, MouseLeftButtonUp, MouseRightButtonDown, MouseRightButtonUp private static void OnPromotedMouseButton(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) { // If it wasn't outside the subtree, we should handle the mouse event. // This makes things consistent so that just in case one of our children // didn't handle the event, it doesn't escape the menu hierarchy. e.Handled = true; } } ////// Called when IsMouseOver changes on this element. /// /// protected override void OnMouseLeave(MouseEventArgs e) { base.OnMouseLeave(e); // if we don't have capture and the mouse left (but the item isn't selected), then we shouldn't have anything selected. if (!HasCapture && !IsMouseOver && CurrentSelection != null && !CurrentSelection.IsKeyboardFocused && !CurrentSelection.IsSubmenuOpen) { CurrentSelection = null; } } // This method ensures that whenever focus is given to an element // within a menu, that the menu enters menu mode. This can't be // done with a simple IsFocusWithin changed handler because we // need to actually enter menu mode before focus changes. private static void OnPreviewKeyboardInputProviderAcquireFocus(object sender, KeyboardInputProviderAcquireFocusEventArgs e) { MenuBase menu = (MenuBase) sender; // If we haven't already pushed menu mode, we need to do it before // focus enters the menu for the first time if (!menu.IsKeyboardFocusWithin && !menu.HasPushedMenuMode) { // Call PushMenuMode just before focus enters the menu... menu.PushMenuMode(/*isAcquireFocusMenuMode*/ true); } } // This method ensures that whenever focus is not acquired // but MenuMode has been pushed with the expection, a // corresponding PopMenu is performed. private static void OnKeyboardInputProviderAcquireFocus(object sender, KeyboardInputProviderAcquireFocusEventArgs e) { MenuBase menu = (MenuBase) sender; if (!menu.IsKeyboardFocusWithin && !e.FocusAcquired && menu.IsAcquireFocusMenuMode) { Debug.Assert(menu.HasPushedMenuMode); // The input provider did not acquire focus. So we will not // succeed in setting focus to the desired element within the // menu. menu.PopMenuMode(); } } ////// Called when the focus is no longer on or within this element. /// /// protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e) { base.OnIsKeyboardFocusWithinChanged(e); if (IsKeyboardFocusWithin) { // When focus enters the menu, we should enter menu mode. if (!IsMenuMode) { IsMenuMode = true; OpenOnMouseEnter = false; } if (KeyboardNavigation.IsKeyboardMostRecentInputDevice()) { // Turn on keyboard cues b/c we took focus with the keyboard KeyboardNavigation.EnableKeyboardCues(this, true); } } else { // Turn off keyboard cues KeyboardNavigation.EnableKeyboardCues(this, false); if (IsMenuMode) { // When showing a ContextMenu of a MenuItem, the ContextMenu will take focus // out of this menu's subtree. The ContextMenu takes capture before taking // focus, so if we are in MenuMode but don't have capture then we are waiting // for the context menu to close. Thus, we should only exit menu mode when // we have capture. if (HasCapture) { IsMenuMode = false; } } else { // Okay, we weren't in menu mode but we could have had a selection (mouse hovering), so clear that if (CurrentSelection != null) { CurrentSelection = null; } } } InvokeMenuOpenedClosedAutomationEvent(IsKeyboardFocusWithin); } private void InvokeMenuOpenedClosedAutomationEvent(bool open) { AutomationEvents automationEvent = open ? AutomationEvents.MenuOpened : AutomationEvents.MenuClosed; if (AutomationPeer.ListenerExists(automationEvent)) { AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(this); if (peer != null) { if (open) { // We raise the event async to allow PopupRoot to hookup Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param) { peer.RaiseAutomationEvent(automationEvent); return null; }), null); } else { peer.RaiseAutomationEvent(automationEvent); } } } } internal static readonly RoutedEvent IsSelectedChangedEvent = EventManager.RegisterRoutedEvent( "IsSelectedChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(MenuBase)); private static void OnIsSelectedChanged(object sender, RoutedPropertyChangedEventArgs e) { // We assume that within a menu the only top-level menu items are direct children of // the one and only top-level menu. MenuItem newSelectedMenuItem = e.OriginalSource as MenuItem; if (newSelectedMenuItem != null) { MenuBase menu = (MenuBase)sender; // If the selected item is a child of ours, make it the current selection. // If the selection changes from a top-level menu item with its submenu // open to another, the new selection's submenu should be open. if (e.NewValue) { if ((menu.CurrentSelection != newSelectedMenuItem) && (newSelectedMenuItem.LogicalParent == menu)) { bool wasSubmenuOpen = false; if (menu.CurrentSelection != null) { wasSubmenuOpen = menu.CurrentSelection.IsSubmenuOpen; menu.CurrentSelection.SetCurrentValueInternal(MenuItem.IsSubmenuOpenProperty, BooleanBoxes.FalseBox); } menu.CurrentSelection = newSelectedMenuItem; if (menu.CurrentSelection != null && wasSubmenuOpen) { // Only open the submenu if it's a header (i.e. has items) MenuItemRole role = menu.CurrentSelection.Role; if (role == MenuItemRole.SubmenuHeader || role == MenuItemRole.TopLevelHeader) { if (menu.CurrentSelection.IsSubmenuOpen != wasSubmenuOpen) { menu.CurrentSelection.SetCurrentValueInternal(MenuItem.IsSubmenuOpenProperty, BooleanBoxes.Box(wasSubmenuOpen)); } } } } } else { // As in MenuItem.OnIsSelectedChanged, if the item is deselected // and it's our current selection, set CurrentSelection to null. if (menu.CurrentSelection == newSelectedMenuItem) { menu.CurrentSelection = null; } } e.Handled = true; } } private bool IsDescendant(DependencyObject node) { return IsDescendant(this, node); } internal static bool IsDescendant(DependencyObject reference, DependencyObject node) { bool success = false; DependencyObject curr = node; while (curr != null) { if (curr == reference) { success = true; break; } // Find popup if curr is a PopupRoot PopupRoot popupRoot = curr as PopupRoot; if (popupRoot != null) { //Now Popup does not have a visual link to its parent (for context menu) //it is stored in its parent's arraylist (DP) //so we get its parent by looking at PlacementTarget Popup popup = popupRoot.Parent as Popup; curr = popup; if (popup != null) { // Try the poup Parent curr = popup.Parent; // Otherwise fall back to placement target if (curr == null) { curr = popup.PlacementTarget; } } } else // Otherwise walk tree { curr = PopupControlService.FindParent(curr); } } return success; } /// /// This is the method that responds to the KeyDown event. /// /// Event arguments protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); Key key = e.Key; switch (key) { case Key.Escape: { if (CurrentSelection != null && CurrentSelection.IsSubmenuOpen) { CurrentSelection.SetCurrentValueInternal(MenuItem.IsSubmenuOpenProperty, BooleanBoxes.FalseBox); OpenOnMouseEnter = false; e.Handled = true; } else { KeyboardLeaveMenuMode(); e.Handled = true; } } break; case Key.System: if ((e.SystemKey == Key.LeftAlt) || (e.SystemKey == Key.RightAlt) || (e.SystemKey == Key.F10)) { KeyboardLeaveMenuMode(); e.Handled = true; } break; } } ////// 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); } ///Create or identify the element used to display the given item. protected override DependencyObject GetContainerForItemOverride() { return new MenuItem(); } #endregion //-------------------------------------------------------------------- // // Private Methods // //-------------------------------------------------------------------- #region Private Methods ////// Called when this element loses capture. /// private static void OnLostMouseCapture(object sender, MouseEventArgs e) { MenuBase menu = sender as MenuBase; // // Use the same technique employed in ComoboBox.OnLostMouseCapture to allow another control in the // application to temporarily take capture and then take it back afterwards. if (Mouse.Captured != menu) { if (e.OriginalSource == menu) { // If capture is null or it's not below the menu, close. // More workaround for task 22022 -- check if it's a descendant (following Logical links too) if (Mouse.Captured == null || !MenuBase.IsDescendant(menu, Mouse.Captured as DependencyObject)) { menu.IsMenuMode = false; } } else { if (MenuBase.IsDescendant(menu, e.OriginalSource as DependencyObject)) { // Take capture if one of our children gave up capture if (menu.IsMenuMode && Mouse.Captured == null && MS.Win32.SafeNativeMethods.GetCapture() == IntPtr.Zero) { Mouse.Capture(menu, CaptureMode.SubTree); e.Handled = true; } } else { menu.IsMenuMode = false; } } } } ////// Called when any menu item within this subtree got clicked. /// Closes all submenus in this tree. /// /// /// private static void OnMenuItemPreviewClick(object sender, RoutedEventArgs e) { MenuBase menu = ((MenuBase)sender); MenuItem menuItemSource = e.OriginalSource as MenuItem; if ((menuItemSource != null) && !menuItemSource.StaysOpenOnClick) { MenuItemRole role = menuItemSource.Role; if (role == MenuItemRole.TopLevelItem || role == MenuItemRole.SubmenuItem) { menu.IsMenuMode = false; e.Handled = true; } } } ////// Called when IsMenuMode changes. /// internal event EventHandler InternalMenuModeChanged { add { EventHandlersStoreAdd(InternalMenuModeChangedKey, value); } remove { EventHandlersStoreRemove(InternalMenuModeChangedKey, value); } } private static readonly EventPrivateKey InternalMenuModeChangedKey = new EventPrivateKey(); ////// Critical - Accesses HwndSource /// TreatAsSafe - No information exposed. Simply decides whether or not to restore focus. /// [SecurityCritical, SecurityTreatAsSafe] private void RestorePreviousFocus() { // Only restore focus if focus is still within the menu. If // focus has already been moved outside of the menu, then // we don't want to disturb it. if (IsKeyboardFocusWithin) { // Only restore WPF focus if the HWND with focus is an // HwndSource. This enables child HWNDs, other top-level // non-WPF HWNDs, or even child HWNDs of other WPF top-level // windows to retain focus when menus are dismissed. IntPtr hwndWithFocus = MS.Win32.UnsafeNativeMethods.GetFocus(); HwndSource hwndSourceWithFocus = hwndWithFocus != IntPtr.Zero ? HwndSource.CriticalFromHwnd(hwndWithFocus) : null; if(hwndSourceWithFocus != null) { // We restore focus by setting focus to the parent's focus // scope. This may not seem correct, because it presumes // the focus came from the logical-focus element of the // parent scope. In fact, it could have come from any // number of places. However, we have not figured out a // better solution for restoring focus across scenarios // such as: // // 1) A context menu of a menu item. // 2) Two menus side-by-side // 3) A menu and a toolbar side-by-side // // Simply remembering the last element with focus and // restoring focus to it does not work. For example, // two menus side-by-side will end up remembering each // other, and you can get stuck in an infinite loop. // // Restoring focus through the parent's focus scope will // not directly work if you open one window's menu from // another window. Visual Studio, as an example, will // intercept the focus change events and forward // appropriately for the scenario of restoring focus to // an element in a different top-level window. // DependencyObject parent = Parent; // if (parent == null) // { // If there is no logical parent, use the visual parent. // parent = VisualTreeHelper.GetParent(this); // } // if (parent != null) // { // IInputElement parentScope = FocusManager.GetFocusScope(parent) as IInputElement; // if (parentScope != null) // { // Keyboard.Focus(parentScope); // } // } // Unfortunately setting focus to the parent focusscope tripped up VS in the scenario where // Menus are contained within ToolBars. In this case when the Menu is dismissed they want // focus to be restored to the element in the main window that previously had focus. However // since ToolBar is the parent focusscope for the Menu we end up restoring focus to its // focusedelment. It is also noted that this implementation is a behavioral change from .Net 3.5. // Hence we are putting back the old behavior which is to set Keyboard.Focus to null which will // delegate focus through the main window to its focusedelement. Keyboard.Focus(null); } else { // In the case where Win32 focus is not on a WPF // HwndSource, we just clear WPF focus completely. // // Note that calling Focus(null) will set focus to the root // element of the active source, which is not what we want. Keyboard.ClearFocus(); } } } // From all of our children, set the InMenuMode property // If turning this property off, recurse to all submenus internal static void SetSuspendingPopupAnimation(ItemsControl menu, MenuItem ignore, bool suspend) { // menu can be either a MenuBase or MenuItem if (menu != null) { int itemsCount = menu.Items.Count; for (int i = 0; i < itemsCount; i++) { MenuItem mi = menu.ItemContainerGenerator.ContainerFromIndex(i) as MenuItem; if (mi != null && mi != ignore && mi.IsSuspendingPopupAnimation != suspend) { mi.IsSuspendingPopupAnimation = suspend; // If leaving menu mode, clear property on all // submenus of this menu if (!suspend) { SetSuspendingPopupAnimation(mi, null, suspend); } } } } } internal void KeyboardLeaveMenuMode() { // If we're in MenuMode, exit. This will relinquish capture, // clear CurrentSelection, and RestorePreviousFocus if (IsMenuMode) { IsMenuMode = false; } else { // CurrentSelection = null; RestorePreviousFocus(); } } #endregion //------------------------------------------------------------------- // // Private Fields // //-------------------------------------------------------------------- #region Private Fields ////// Currently selected item in this menu or submenu. /// ///internal MenuItem CurrentSelection { get { return _currentSelection; } set { // Even if we don't have capture we should move focus when one item is already focused. bool wasFocused = false; if (_currentSelection != null) { wasFocused = _currentSelection.IsKeyboardFocused; _currentSelection.SetCurrentValueInternal(MenuItem.IsSelectedProperty, BooleanBoxes.FalseBox); } _currentSelection = value; if (_currentSelection != null) { _currentSelection.SetCurrentValueInternal(MenuItem.IsSelectedProperty, BooleanBoxes.TrueBox); if (wasFocused) { _currentSelection.Focus(); } } } } internal bool HasCapture { get { return Mouse.Captured == this; } } internal bool IgnoreNextLeftRelease { get { return _bitFlags[(int)MenuBaseFlags.IgnoreNextLeftRelease]; } set { _bitFlags[(int)MenuBaseFlags.IgnoreNextLeftRelease] = value; } } internal bool IgnoreNextRightRelease { get { return _bitFlags[(int)MenuBaseFlags.IgnoreNextRightRelease]; } set { _bitFlags[(int)MenuBaseFlags.IgnoreNextRightRelease] = value; } } internal bool IsMenuMode { get { return _bitFlags[(int)MenuBaseFlags.IsMenuMode]; } set { Debug.Assert(CheckAccess(), "IsMenuMode requires context access"); bool isMenuMode = _bitFlags[(int)MenuBaseFlags.IsMenuMode]; if (isMenuMode != value) { isMenuMode = _bitFlags[(int)MenuBaseFlags.IsMenuMode] = value; if (isMenuMode) { // Take capture so that all mouse messages stay below the menu. if (!IsDescendant(this, Mouse.Captured as Visual) && !Mouse.Capture(this, CaptureMode.SubTree)) { // If we're unable to take capture, leave menu mode immediately. isMenuMode = _bitFlags[(int)MenuBaseFlags.IsMenuMode] = false; } else { // If we haven't pushed the menu mode yet (which // should have already happened if keyboard focus // is set within the menu), push it now. if (!HasPushedMenuMode) { PushMenuMode(/*isAcquireFocusMenuMode*/ false); } RaiseClrEvent(InternalMenuModeChangedKey, EventArgs.Empty); } } if (!isMenuMode) { bool wasSubmenuOpen = false; if (CurrentSelection != null) { wasSubmenuOpen = CurrentSelection.IsSubmenuOpen; CurrentSelection.IsSubmenuOpen = false; CurrentSelection = null; } // Note that this code path is also used to cleanup // the case where setting IsMenuMode=true fails due // to failure to gain capture. We pop out of the menu // mode irrespective of where it was pushed. if (HasPushedMenuMode) { // Call PopMenuMode before we do much else, so that // focus changes will properly activate windows. PopMenuMode(); } if (!value) { // Fire the event before capture is released and after submenus have been closed. RaiseClrEvent(InternalMenuModeChangedKey, EventArgs.Empty); } // Clear suspending animation flags on all descendant menuitems SetSuspendingPopupAnimation(this, null, false); // if (HasCapture) { Mouse.Capture(null); } RestorePreviousFocus(); } // Assume menu items should open when the mouse hovers over them OpenOnMouseEnter = isMenuMode; } } } // This bool is used by top level menu items to // determine if they should open on mouse enter // Menu items shouldn't open if the use hit Alt // to get in menu mode and then hovered over the item internal bool OpenOnMouseEnter { get { return _bitFlags[(int)MenuBaseFlags.OpenOnMouseEnter]; } set { _bitFlags[(int)MenuBaseFlags.OpenOnMouseEnter] = value; } } /// /// Critical - Accesses PresentationSource /// TreatAsSafe - No information exposed. Simply notifies the /// PresentationSource to enter/leave menu mode. /// [SecurityCritical, SecurityTreatAsSafe] private void PushMenuMode(bool isAcquireFocusMenuMode) { Debug.Assert(_pushedMenuMode == null); _pushedMenuMode = PresentationSource.CriticalFromVisual(this); Debug.Assert(_pushedMenuMode != null); IsAcquireFocusMenuMode = isAcquireFocusMenuMode; InputManager.UnsecureCurrent.PushMenuMode(_pushedMenuMode); } ////// Critical - Accesses PresentationSource /// TreatAsSafe - No information exposed. Simply notifies the /// PresentationSource to enter/leave menu mode. /// [SecurityCritical, SecurityTreatAsSafe] private void PopMenuMode() { Debug.Assert(_pushedMenuMode != null); PresentationSource pushedMenuMode = _pushedMenuMode; _pushedMenuMode = null; IsAcquireFocusMenuMode = false; InputManager.UnsecureCurrent.PopMenuMode(pushedMenuMode); } ////// Critical - Accesses PresentationSource /// TreatAsSafe - No information exposed. Simply returns /// whether or not we have pushed menu mode. /// private bool HasPushedMenuMode { [SecurityCritical, SecurityTreatAsSafe] get { return _pushedMenuMode != null; } } ////// This boolean determines if the PushMenuMode was /// performed due to acquire focus or due to programmatic /// set of IsMenuMode. /// private bool IsAcquireFocusMenuMode { get { return _bitFlags[(int)MenuBaseFlags.IsAcquireFocusMenuMode]; } set { _bitFlags[(int)MenuBaseFlags.IsAcquireFocusMenuMode] = value; } } [SecurityCritical] private PresentationSource _pushedMenuMode; private MenuItem _currentSelection; private BitVector32 _bitFlags = new BitVector32(0); private enum MenuBaseFlags { IgnoreNextLeftRelease = 0x01, IgnoreNextRightRelease = 0x02, IsMenuMode = 0x04, OpenOnMouseEnter = 0x08, IsAcquireFocusMenuMode = 0x10, } #endregion // Notes: // We want to enforce: // 1) IsKeyboardFocused -> IsHighlighted // 2) IsKeyboardFocusWithin -> Mouse.Captured is an ancestor of Keyboard.FocusedElement // 3) IsSubmenuOpen -> IsHighlighted and IsKeyboardFocusWithin // these conditions are violated only in the case of mousing from one submenu to another and there is a delay. } } // 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
- FixedPageProcessor.cs
- IntegerCollectionEditor.cs
- FontFamilyIdentifier.cs
- Expression.cs
- httpstaticobjectscollection.cs
- odbcmetadatacollectionnames.cs
- SoapExtension.cs
- DetailsView.cs
- GlobalEventManager.cs
- SecurityPermission.cs
- CmsUtils.cs
- TreeViewImageIndexConverter.cs
- Reference.cs
- UndoManager.cs
- QueryCursorEventArgs.cs
- BaseTemplateCodeDomTreeGenerator.cs
- FilterEventArgs.cs
- embossbitmapeffect.cs
- BuiltInPermissionSets.cs
- BufferedGraphicsManager.cs
- BitVec.cs
- AjaxFrameworkAssemblyAttribute.cs
- odbcmetadatacollectionnames.cs
- WeakReference.cs
- AssemblyNameProxy.cs
- OleDbDataAdapter.cs
- SpecialNameAttribute.cs
- XmlSchemaValidator.cs
- Keywords.cs
- Int16Converter.cs
- ObjectParameter.cs
- ItemsChangedEventArgs.cs
- SqlAliasesReferenced.cs
- SharedPersonalizationStateInfo.cs
- RegisteredDisposeScript.cs
- OletxDependentTransaction.cs
- shaperfactory.cs
- AdCreatedEventArgs.cs
- XpsFont.cs
- BrushMappingModeValidation.cs
- PolicyImporterElementCollection.cs
- DataBindingList.cs
- SpellerError.cs
- GridViewDeleteEventArgs.cs
- SystemPens.cs
- RotationValidation.cs
- ConnectionPoolManager.cs
- Comparer.cs
- IUnknownConstantAttribute.cs
- DataKey.cs
- storepermission.cs
- SHA256.cs
- PersonalizationProviderCollection.cs
- AnnotationStore.cs
- WhitespaceReader.cs
- MarshalByValueComponent.cs
- DbConnectionPoolGroup.cs
- MultipleViewPatternIdentifiers.cs
- FreezableCollection.cs
- ListenerTraceUtility.cs
- NavigationExpr.cs
- ResourceType.cs
- glyphs.cs
- BaseCodePageEncoding.cs
- FlowchartSizeFeature.cs
- RegisteredDisposeScript.cs
- XmlTypeAttribute.cs
- IChannel.cs
- QuerySafeNavigator.cs
- Utilities.cs
- ScriptServiceAttribute.cs
- CustomExpression.cs
- FrameworkElement.cs
- ISAPIWorkerRequest.cs
- wgx_sdk_version.cs
- PiiTraceSource.cs
- UndoUnit.cs
- Cloud.cs
- SoapEnvelopeProcessingElement.cs
- UIEndRequest.cs
- ColorContext.cs
- ServiceBusyException.cs
- IsolatedStorageException.cs
- PropertyMapper.cs
- SafeLibraryHandle.cs
- SqlDataSourceWizardForm.cs
- GridViewRow.cs
- FormClosedEvent.cs
- StorageInfo.cs
- DetailsViewCommandEventArgs.cs
- URI.cs
- HttpRequestTraceRecord.cs
- HandleCollector.cs
- ElementFactory.cs
- ScriptManager.cs
- SchemaComplexType.cs
- InvokeGenerator.cs
- TextPenaltyModule.cs
- _Events.cs
- WindowsComboBox.cs