MenuBase.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Controls / Primitives / MenuBase.cs / 1 / 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.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));

            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;
            }
        }
 

        ///  
        /// 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.IsSubmenuOpen = false;
                        }
 
                        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.IsSubmenuOpen = 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.IsSubmenuOpen = false;
                            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();

        private void RestorePreviousFocus() 
        {
            if (IsKeyboardFocusWithin) 
            { 
                Keyboard.Focus(null);
            } 

            // Menus should not have focus returned to them automatically
            DependencyObject parent = Parent;
            if (parent != null) 
            {
                DependencyObject parentScope = FocusManager.GetFocusScope(parent); 
                if (parentScope != null) 
                {
                    IInputElement focusedElement = FocusManager.GetFocusedElement(parentScope); 
                    if (ElementIsWithin(focusedElement))
                    {
                        FocusManager.SetFocusedElement(parentScope, null);
                    } 
                }
            } 
        } 

        private bool ElementIsWithin(IInputElement element) 
        {
            if (element == this)
            {
                return true; 
            }
 
            MenuItem menuItem = element as MenuItem; 
            while (menuItem != null)
            { 
                DependencyObject parent = menuItem.Parent;
                if (parent == null)
                {
                    parent = ItemsControl.ItemsControlFromItemContainer(menuItem); 
                }
 
                if (parent == this) 
                {
                    return true; 
                }
                else
                {
                    menuItem = parent as MenuItem; 
                }
            } 
 
            return false;
        } 

        // 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.IsSelected = false;
                } 

                _currentSelection = value;
                if (_currentSelection != null)
                { 
                    _currentSelection.IsSelected = true;
                    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
                        { 
                            RaiseClrEvent(InternalMenuModeChangedKey, EventArgs.Empty);
                        } 
                    } 

                    if (!isMenuMode) 
                    {
                        bool wasSubmenuOpen = false;

                        if (CurrentSelection != null) 
                        {
                            wasSubmenuOpen = CurrentSelection.IsSubmenuOpen; 
                            CurrentSelection.IsSubmenuOpen = false; 
                            CurrentSelection = null;
                        } 

                        // 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; } 
        }
 
        private MenuItem _currentSelection;
        private BitVector32 _bitFlags = new BitVector32(0);

        private enum MenuBaseFlags 
        {
            IgnoreNextLeftRelease  = 0x01, 
            IgnoreNextRightRelease = 0x02, 
            IsMenuMode             = 0x04,
            OpenOnMouseEnter       = 0x08, 
        }

        #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.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));

            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;
            }
        }
 

        ///  
        /// 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.IsSubmenuOpen = false;
                        }
 
                        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.IsSubmenuOpen = 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.IsSubmenuOpen = false;
                            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();

        private void RestorePreviousFocus() 
        {
            if (IsKeyboardFocusWithin) 
            { 
                Keyboard.Focus(null);
            } 

            // Menus should not have focus returned to them automatically
            DependencyObject parent = Parent;
            if (parent != null) 
            {
                DependencyObject parentScope = FocusManager.GetFocusScope(parent); 
                if (parentScope != null) 
                {
                    IInputElement focusedElement = FocusManager.GetFocusedElement(parentScope); 
                    if (ElementIsWithin(focusedElement))
                    {
                        FocusManager.SetFocusedElement(parentScope, null);
                    } 
                }
            } 
        } 

        private bool ElementIsWithin(IInputElement element) 
        {
            if (element == this)
            {
                return true; 
            }
 
            MenuItem menuItem = element as MenuItem; 
            while (menuItem != null)
            { 
                DependencyObject parent = menuItem.Parent;
                if (parent == null)
                {
                    parent = ItemsControl.ItemsControlFromItemContainer(menuItem); 
                }
 
                if (parent == this) 
                {
                    return true; 
                }
                else
                {
                    menuItem = parent as MenuItem; 
                }
            } 
 
            return false;
        } 

        // 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.IsSelected = false;
                } 

                _currentSelection = value;
                if (_currentSelection != null)
                { 
                    _currentSelection.IsSelected = true;
                    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
                        { 
                            RaiseClrEvent(InternalMenuModeChangedKey, EventArgs.Empty);
                        } 
                    } 

                    if (!isMenuMode) 
                    {
                        bool wasSubmenuOpen = false;

                        if (CurrentSelection != null) 
                        {
                            wasSubmenuOpen = CurrentSelection.IsSubmenuOpen; 
                            CurrentSelection.IsSubmenuOpen = false; 
                            CurrentSelection = null;
                        } 

                        // 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; } 
        }
 
        private MenuItem _currentSelection;
        private BitVector32 _bitFlags = new BitVector32(0);

        private enum MenuBaseFlags 
        {
            IgnoreNextLeftRelease  = 0x01, 
            IgnoreNextRightRelease = 0x02, 
            IsMenuMode             = 0x04,
            OpenOnMouseEnter       = 0x08, 
        }

        #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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK