65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 3.0 下拉菜单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (18投票s)

2009年4月15日

GPL3

4分钟阅读

viewsIcon

135252

downloadIcon

1312

这是 SL 3.0 下拉菜单的实现,类似于应用程序主菜单。

示例图片

Main.png

打开的菜单项

Main-Opened_1_.png

---

Main-Opened_2_.png

支持复选框和单选按钮(分组) 

Main-Opened_3_.png

快速菜单项访问(下划线字符) 

Main-Opened_4_.png

Main-Opened_5_.png

在线演示说明  

菜单激活快捷键设置为“Ctrl”键。按下菜单激活键时,具有快速访问的菜单项会显示下划线字符。如果您使用“Ctrl”键激活菜单,只需在释放“Ctrl”键的同时按下带下划线的字符即可打开菜单项。

引言    

首先,对于您在阅读本文时可能遇到的语言问题,我深表歉意。英语是我的第二语言,因此对于任何拼写错误或语言错误,我都很抱歉。

如果您查看文章中的图片,我认为无需解释这是什么。我驱动的主要想法是开发可能很有用的基本应用程序功能。我认为每个人都明白 Silverlight 使我们能够实现类似 Windows 的应用程序,当涉及到应用程序命令导航时,我的菜单可能会非常方便。 

背景

这个控件的实现是我正在开发的一个业务应用程序的一部分。整个应用程序包含许多附加类和控件,我将在稍后发布。最终的包将包括:

  1. WindowBase 类(模态模式支持) - 正在开发中...
  2. 工具栏 - 正在开发中...  
  3. 桌面(类似 Windows 的桌面模拟) - 正在开发中...
  4. 任务栏  - 正在开发中
  5. DesktopApplication(这是用于桌面插件开发的基类)- 正在开发中...

开发工具

编译和运行此示例所需的开发工具列在下面:

  1. Microsoft Visual Studio 2008
  2. Microsoft Expression Blend 3.0 (RCW)
  3. Silverlight SDK 3.0 (RCW)
  4. Microsoft Visual Studio Tools for Silverlight 3.0
  5. Microsoft Silverlight Toolkit 2009 年 7 月版
  6. Silverlight 3.0 RCW 开发者运行时

主要特点

  • 鼠标导航
  • 键盘导航
  • 快捷键支持
  • 快速菜单项访问(类似于快捷键)
  • 混合导航模式(鼠标+键盘)
  • 复选框支持
  • 单选按钮支持(菜单项分组)

注意(快捷键支持限制): 

让我解释一下使用快捷键功能。当您的应用程序注册快捷键时,它可能不起作用。发生这种情况是因为浏览器本身会处理特定的快捷键。一个小例子。我在我的示例应用程序中注册了一个快捷键作为动作(关闭 - Ctrl+E)。我从未收到事件到 SL 输入子系统,因为浏览器处理了此事件,导致浏览器窗口顶部的搜索栏被激活。

关于使用快捷键,您需要知道的第二件事是,输入子系统在全屏模式下限制键盘导航。

代码背景  

代码包含以下类: 

  1. Menu - 根菜单实现 
  2. MenuItem - 菜单项实现
  3. MenuTracker - 此对象跟踪鼠标/键盘事件,负责菜单项/子菜单项弹出功能/键盘导航
  4. MenuKeyGesture - 菜单项快捷键容器
  5. MenuKeyGestureTypeConverter - 菜单项 XAML 快捷键字符串转换器
  6. ItemAccessKey - 菜单项快速访问键容器
  7. TypeExtensions - 类型扩展方法

功能最强大的代码写在 MenuItem/MenuTracker 类中。

<Menu> 类 

[TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
public class Menu : ItemsControl, IItemsControl
{
    /// <summary>Menu Tracking object instance</summary>
    internal MenuTracker        _menu_tracker;
    /// <summary>Global Menu instance list</summary>
    internal static List<Menu>  _instances;

    /// <summary>Menu Activation Key Gesture ( Default is Key.Ctrl )</summary>
    public static readonly DependencyProperty ActivationKeyGestureProperty = 
	DependencyProperty.Register("ActivationKeyGesture", 
	typeof(MenuKeyGesture), typeof(Menu),
      new PropertyMetadata(new MenuKeyGesture() 
	{ Gesture = Key.Ctrl, ModifierKey = ModifierKeys.None },
        delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }));

    /// <summary>Static field initialization</summary>
    static Menu() {
        _instances = new List<Menu>();
    }

    /// <summary>Public class constructor</summary>
    public Menu(){
        //Setting default style key
        base.DefaultStyleKey = typeof(Menu);
        //Hooking loaded event
        Loaded += delegate(object sender, RoutedEventArgs e)
        {
            _menu_tracker = new MenuTracker(this, this, this.GetRoot(), true);
        };
        //We have to add this instance to global instance list for futhure processing
        _instances.Add(this);
    }

    /// <summary>Class Destructor</summary>
    ~Menu()
    {
        //Removing this instance from global instance list
        _instances.Remove(this);
    }

    Guid _uniq_anime_guid = Guid.NewGuid();

    public void AnimatePopup(Popup popup)
    {
        //sample animation - future implementation will possible have templates...
        Storyboard sb = null;
        popup.Child.Opacity = 0;
        if (popup.Resources.Contains(_uniq_anime_guid.ToString()) == false)
        {
            Duration duration = new Duration(TimeSpan.FromMilliseconds(250));
            DoubleAnimation opacity_animation   = 
		new DoubleAnimation() { Duration = duration };
            sb = new Storyboard() { Duration    = duration };
            sb.Children.Add(opacity_animation);
            Storyboard.SetTarget(opacity_animation, popup.Child);
            Storyboard.SetTargetProperty(opacity_animation, 
			new PropertyPath("(UIElement.Opacity)"));
            opacity_animation.To   = 1;
            popup.Resources.Add(_uniq_anime_guid.ToString(), sb);
            sb.Begin();
        }
        else
        {
            sb = popup.Resources[_uniq_anime_guid.ToString()] as Storyboard;
            sb.Begin();
        }
    }

    /// <summary>Closes all opened item's popup windows in all menu instances</summary>
    public static void CloseAllPopups()
    {
        _instances.ForEach(menu =>
            {
                try
                {

                    menu.ClosePopups();
                }
                catch { }
            });
    }

    /// <summary>Returns an item container as MenuItem object</summary>
    /// <returns>New instance of MenuItem object</returns>
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MenuItem() { };
    }

    /// <summary>Determines if item is MenuItem instance</summary>
    /// <param name="item">item to check</param>
    /// <returns>True - the item is MenuItem, otherwise false</returns>
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return item is MenuItem;
    }

    /// <summary>Prepares an item container - container initialization</summary>
    /// <param name="element">Container object</param>
    /// <param name="item">Menu item</param>
    protected override void PrepareContainerForItemOverride
				(DependencyObject element, object item)
    {
        if (element is MenuItem)
        {
            MenuItem container = (element as MenuItem);
            if (IsItemItsOwnContainerOverride(item) == false)
                container.Header    = item;
            container.Menu          = this;
            container.TopLevel      = true;
        }
        base.PrepareContainerForItemOverride(element, item);
    }

    /// <summary>Closes all opened item's popup windows</summary>
    internal void ClosePopups()
    {
        this.Items.OfType<MenuItem>().ToList().ForEach(item =>
            {
                _menu_tracker.CloseAllChildPopups(item, true);
            });
    }

    /// <summary>Gets menu tracker instance</summary>
    internal MenuTracker Tracker
    {
        get { return _menu_tracker; }
    }

    /// <summary>Gets or sets Menu activation key gesture</summary>
    public MenuKeyGesture ActivationKeyGesture
    {
        get { return (MenuKeyGesture)GetValue(ActivationKeyGestureProperty); }
        set { SetValue(ActivationKeyGestureProperty, value); }
    }

    /// <summary>Gets Items collection for internal use</summary>
    IEnumerable<MenuItem> IItemsControl.InternalItems
    {
        get { return Items.OfType<MenuItem>(); }
    }
}

此类包含一个非功能性的属性“Orientation”。:O) 留待以后实现...

如果您查看代码,您会看到一个名为“_instances”的static字段。它在“MenuTracker”对象中使用,用于在用户单击页面上的任何位置(且不是 MenuItem 对象)时关闭多实例 Menu 类的所有已打开菜单项。

控件的样式在类构造函数中完成: 

 base.DefaultStyleKey = typeof(Menu); 

所有样式都必须存储在名为“Themes”的解决方案文件夹中,文件名是“generic.xaml”。
让我们看看 Menu 样式

<!--MENU STYLE-->
<Style TargetType="local:Menu">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:Menu">
                <Grid x:Name="RootElement" Visibility="Visible" Background="#9999DDF0" >
                    <Border Background="{TemplateBinding Background}" 
			BorderBrush="{TemplateBinding BorderBrush}" 
			BorderThickness="{TemplateBinding BorderThickness}" 
			Margin="{TemplateBinding Margin}" 
			Padding="{TemplateBinding Padding}">
                        <ItemsPresenter/>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <toolkit:WrapPanel ItemHeight="25" Margin="2,2,2,2" 
			HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

命名空间前缀代表本地项目命名空间

xmlns:local="clr-namespace:System.Windows.Controls;assembly=DropDownMenu"

<MenuItem> 类

让我们看看 MenuItem 类: 

[
    TemplatePart(Name = "RootElement",      Type = typeof(FrameworkElement)),
    TemplatePart(Name = "PART_Popup",       Type = typeof(Popup)),
    TemplatePart(Name = "PART_PopupTopRect",Type = typeof(Rectangle)),
    TemplatePart(Name = "PART_Selected",    Type = typeof(FrameworkElement)),
    TemplatePart(Name = "PART_Opened",      Type = typeof(FrameworkElement)),
    TemplatePart(Name = "PART_Header",      Type = typeof(ContentControl)),
    TemplatePart(Name = "PART_LeftBar",     Type = typeof(FrameworkElement)),
    TemplatePart(Name = "PART_HitTest",     Type = typeof(FrameworkElement)),
    TemplatePart(Name = "PART_Separator",   Type = typeof(FrameworkElement)),
    TemplatePart(Name = "PART_SubMenuGrip", Type = typeof(FrameworkElement)),
    ContentProperty("Items")
]
public class MenuItem : Control, IItemsControl
{
    /// <summary>This is a container object for fast menu item access 
    /// (shortcut)</summary>
    public class ItemAccessKey{
        public string Key       { get; set; }
        public Border Border    { get; set; }
    }

    private     System.Windows.Controls.Primitives.Popup _popup;
    private     ContentControl      _HeaderControl;
    private     FrameworkElement    _PART_Seperator, 
				_PART_Selected, _PART_Opened, _RootElement;
    private     Rectangle           _PART_LeftBar, _PART_PopupTopRect, _PART_HitTest;
    private     FrameworkElement    _PART_SubMenuGrip;
    internal    MenuTracker         _menu_tracker;
    private     ItemAccessKey       _accessKey = null;
    private     bool                _is_highlighted;
    private     bool                _is_Opended;

    public static readonly DependencyProperty CommandProperty = 
	DependencyProperty.RegisterAttached("Command", 
	typeof(ICommand), typeof(MenuItem),
    	new PropertyMetadata(null, delegate(DependencyObject d, 
	DependencyPropertyChangedEventArgs e){
    }));
    
    public static readonly DependencyProperty IsCheckedProperty = 
	DependencyProperty.RegisterAttached("IsChecked", typeof(bool), typeof(MenuItem),
    new PropertyMetadata(false, delegate(DependencyObject d, 
				DependencyPropertyChangedEventArgs e)
    {
        MenuItem item = d as MenuItem;
        if ( item != null && string.IsNullOrEmpty(item.GroupName) == 
					false && ((bool)e.NewValue) )
        {
            //group name exists
            IEnumerable<MenuItem> gr_items = null;
            if (item.ParentItem != null)
            {
                gr_items = (from i in item.ParentItem.Items 
                                                  where i.GroupName == item.GroupName 
                                                  select i);
                if ( gr_items.Count() > 0 )
                {
                    gr_items = gr_items.Where(i => i != item && i.IsChecked);
                    gr_items.ToList().ForEach(i => { i.IsChecked = false; });
                }
            }
            else if( item.Menu != null )
            {
                gr_items = (from i in item.Menu.Items.Cast<MenuItem>()
                            where i.GroupName == item.GroupName
                            select i);
                if (gr_items.Count() > 0)
                {
                    gr_items = gr_items.Where(i => i != item && i.IsChecked);
                    gr_items.ToList().ForEach(i => { i.IsChecked = false; });
                }                   
            }
        }
    }));

    public static readonly DependencyProperty GroupNameProperty = 
	DependencyProperty.RegisterAttached("GroupName", 
	typeof(string), typeof(MenuItem),
        	new PropertyMetadata(null, delegate(DependencyObject d, 
	DependencyPropertyChangedEventArgs e)
        {
        }));

    public static readonly DependencyProperty ItemsProperty = 
	DependencyProperty.RegisterAttached("Items", 
	typeof(ObservableCollection<MenuItem>), typeof(MenuItem),
    	new PropertyMetadata(null, delegate(DependencyObject d, 
	DependencyPropertyChangedEventArgs e){

    }));

    public static readonly DependencyProperty TopLevelProperty = 
	DependencyProperty.RegisterAttached("TopLevel", typeof(bool), typeof(MenuItem),
	new PropertyMetadata(delegate(DependencyObject d, 
	DependencyPropertyChangedEventArgs e){
    }));

    public static readonly DependencyProperty HeaderProperty = 
	DependencyProperty.RegisterAttached("Header", typeof(object), typeof(MenuItem),
    new PropertyMetadata(delegate(DependencyObject d, 
		DependencyPropertyChangedEventArgs e){
    }));
    public static readonly DependencyProperty IsMouseOverProperty = 
	DependencyProperty.Register("IsMouseOver", 
			typeof(bool), typeof(MenuItem), null);

    public static readonly DependencyProperty KeyGestureProperty = 
	DependencyProperty.Register("KeyGesture", 
	typeof(MenuKeyGesture), typeof(MenuItem),
      	new PropertyMetadata(new MenuKeyGesture() 
	{ Gesture = Key.None, ModifierKey = ModifierKeys.None },
        	delegate(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MenuItem item = d as MenuItem;
            if (item != null)
            {
                if (e.NewValue != null)
                {
                    MenuKeyGesture gestute = e.NewValue as MenuKeyGesture;
                    if (gestute.Gesture != Key.None)
                    {
                        System.Windows.Automation.AutomationProperties.
				SetAcceleratorKey(item, gestute.ToString());
                    }
                }
            }
        }));

    public event RoutedEventHandler Click;
    public event RoutedEventHandler Opening;
    public event RoutedEventHandler Opened;

    public MenuItem()
    {
        TopLevel = false;
        SetValue(ItemsProperty, new ObservableCollection<MenuItem>());
        Items.CollectionChanged += 
	new System.Collections.Specialized.NotifyCollectionChangedEventHandler
	(OnItems_CollectionChanged);
        base.DefaultStyleKey = typeof(MenuItem);
    }

    protected void OnItems_CollectionChanged(object sender, 
	System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
                if (e.NewItems != null)
                    e.NewItems.Cast<MenuItem>().ToList().ForEach(item =>
                        {
                            if (TopLevel == false && Items.Count > 0 && 
				_PART_SubMenuGrip != null)
                                _PART_SubMenuGrip.Visibility = Visibility.Visible;
                            item.TopLevel = false;
                            item.Menu       = this.Menu;
                            item.ParentItem = this;
                        });
                break;
        }
    }

    protected void InitializeChildren(MenuItem item)
    {
        item.Items.ToList().ForEach(i =>
            {
                i.Menu = Menu;
                i.ParentItem = item;
                InitializeChildren(i);
            });
    }
    
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        
        _RootElement        = (FrameworkElement)GetTemplateChild("RootElement");
        _popup              = (Popup)GetTemplateChild("PART_Popup");
        _PART_PopupTopRect  = (Rectangle)GetTemplateChild("PART_PopupTopRect");
        _popup.IsOpen       = false;
        _PART_Selected      = (FrameworkElement)GetTemplateChild("PART_Selected");
        _PART_Opened        = (FrameworkElement)GetTemplateChild("PART_Opened");
        _HeaderControl      = (ContentControl)GetTemplateChild("PART_Header");
        _PART_LeftBar       = (Rectangle)GetTemplateChild("PART_LeftBar");
        _PART_HitTest       = (Rectangle)GetTemplateChild("PART_HitTest");
        _PART_Seperator     = (FrameworkElement)GetTemplateChild("PART_Separator");
        _PART_SubMenuGrip   = (FrameworkElement)GetTemplateChild("PART_SubMenuGrip");
        _menu_tracker       = new MenuTracker(Menu, this, 
				_popup.Child as FrameworkElement, false);

        InitializeChildren(this);

        if (TopLevel == false)
        {
            if ( Items.Count > 0 && _PART_SubMenuGrip != null )
                _PART_SubMenuGrip.Visibility = Visibility.Visible;
            if (_RootElement != null)
            {
                bool IsSeparator = Header != null && Header.ToString().Equals("-");
                _RootElement.Height = Header != null && 
			Header.ToString().Equals("-") ? 7 : 26;
                if (IsSeparator)
                    _PART_Seperator.Visibility = Visibility.Visible;
                else
                    _PART_Seperator.Visibility = Visibility.Collapsed;
            }
            if (_HeaderControl != null)
            {
                _HeaderControl.HorizontalAlignment = HorizontalAlignment.Left;
                _HeaderControl.Margin = new Thickness(4, 0, 0, 0);
            }
            if (_PART_LeftBar != null)
                _PART_LeftBar.Visibility = Visibility.Visible;
            if (_PART_Opened != null && _PART_Opened is Border)
                (_PART_Opened as Border).CornerRadius = new CornerRadius(2, 0, 2, 2);
        }
        else
        {
            if (_RootElement is Grid)
                (_RootElement as Grid).ColumnDefinitions[2].Width = new GridLength(6d);
            _PART_Seperator.Visibility = Visibility.Collapsed;
            if (_PART_Opened != null && _PART_Opened is Border)
                (_PART_Opened as Border).CornerRadius = new CornerRadius(2, 2, 0, 2);
            if (_HeaderControl != null)
                _HeaderControl.Margin = new Thickness(-17, 0, 0, 0);
        }

        if (IsHighlighted)
        {
            if (_PART_Selected != null)
                _PART_Selected.Visibility = Visibility.Visible;
        }

        bool    IsHeaderTextFound   = false;
        string  HeaderText          = string.Empty;

        if (Header != null && typeof(string) == 
	Header.GetType() && Header.ToString().Length >= 2 &&
          Header.ToString().Contains('_'))
        {
            IsHeaderTextFound = true;
            HeaderText = Header.ToString();
        }
        else if ( Header != null && Header is FrameworkElement )
        {
            HeaderText = FindAccessKeyHeaderControlText(Header as FrameworkElement);
            if (string.IsNullOrEmpty(HeaderText) == false 
		&& HeaderText.Length >= 2 && HeaderText.Contains('_'))
                IsHeaderTextFound = true;
            else
                HeaderText = null;
        }

        if( IsHeaderTextFound )
        {
            char[] h_text = HeaderText.ToCharArray();
            Grid h_panel = new Grid() { };

            if (Header is string)
                Header = h_panel;
            else
            {
                //this is a point of interest, because we have 
	       //to replace text control with our template...
                FrameworkElement h_control = 
		FindAccessKeyHeaderControl(Header as FrameworkElement);
                if (h_control is ContentControl)
                    (h_control as ContentControl).Content = h_panel;
                else
                    return;
            }

            double x = 0;
            TextBlock t = new TextBlock()
            {
                Text = "",
                FontFamily = FontFamily,
                FontSize = FontSize,
                FontWeight = FontWeight,
                FontStyle = FontStyle,
                FontStretch = FontStretch,
                Foreground = Foreground,
                Margin = new Thickness(0, 0, 0, 0),
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Left
            };

            h_panel.Children.Add(t);
            double last_width = 0;
            for (int i = 0; i < h_text.Length; i++)
            {
                last_width = t != null ? t.ActualWidth : 0;

                if (h_text[i].ToString().Equals("_") == false)
                {
                    Run run = new Run()
                    {
                        Text = h_text[i].ToString(),
                        FontFamily = FontFamily,
                        FontSize = FontSize,
                        FontWeight = FontWeight,
                        FontStyle = FontStyle,
                        FontStretch = FontStretch,
                        Foreground = Foreground,
                    };
                    t.Inlines.Add(run);
                }
                if (_accessKey == null && h_text[i].ToString().Equals("_"))// Access Key
                {
                    Run run = new Run()
                    {
                        Text = h_text[i + 1].ToString(),
                        FontFamily = FontFamily,
                        FontSize = FontSize,
                        FontWeight = FontWeight,
                        FontStyle = FontStyle,
                        FontStretch = FontStretch,
                        Foreground = Foreground,
                    };
                    t.Inlines.Add(run);
                    if (t != null)
                        t.UpdateLayout();
                    this.UpdateLayout();
                    _accessKey = new ItemAccessKey()
                    {
                        Border = new Border()
                        {
                            BorderThickness = new Thickness(0, 0, 0, 1),
                            BorderBrush = Foreground,
                            Margin = new Thickness(x, t != null ? 
					t.ActualHeight - 4 : 0, 0, 0),
                            VerticalAlignment = VerticalAlignment.Top,
                            HorizontalAlignment = HorizontalAlignment.Left,
                            Width = t.ActualWidth - last_width,
                            Height = 2
                        },
                        Key = i + 1 < h_text.Length ? h_text[i + 1].ToString() : ""
                    };
                    h_panel.Children.Add(_accessKey.Border);
                    i++;
                }
                if (t != null)
                    t.UpdateLayout();
                this.UpdateLayout();
                x += t != null ? (t.ActualWidth + 0.1) - last_width : 0;
            }
            if (_accessKey != null)
                _accessKey.Border.Opacity = 0.0d;
        }
        else if (Header != null && typeof(string) == Header.GetType() 
	&& /*This is a separator string*/ Header.ToString().Equals("-") == false )
        {
            TextBlock t = null;
            t = new TextBlock()
            {
                Text = Header.ToString(),
                FontFamily = FontFamily,
                FontSize = FontSize,
                FontWeight = FontWeight,
                FontStyle = FontStyle,
                FontStretch = FontStretch,
                Foreground = Foreground,
                Margin = new Thickness(0, 0, 0, 0),
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Left
            };
            Header = t;
        }
    }

    /// <summary>This method searches an element in Header control tree 
    /// for ContentControl/TextBlock control</summary>
    /// <param name="element">Header root element</param>
    /// <returns>control text if exist</returns>
    public string FindHeaderControlText(FrameworkElement element)
    {
        int count = VisualTreeHelper.GetChildrenCount(element);
        string ret_data = null;
        if (count > 0)
        {
            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(element, i);
                if (child is FrameworkElement)
                {
                    if (child is ContentControl || child is TextBlock)
                    {
                        if (child is TextBlock)
                        {
                            ret_data = (child as TextBlock).Text;
                            return ret_data;
                        }
                        else if (child is ContentControl)
                        {
                            if ((child as ContentControl).Content != null && 
				((child as ContentControl).Content is string))
                                ret_data = (child as ContentControl).Content.ToString();
                            else if ((child as ContentControl).Content 
						is FrameworkElement)
                            {
                                ret_data = FindHeaderControlText((child 
				as ContentControl).Content as FrameworkElement);
                            }
                        }
                        if( string.IsNullOrEmpty(ret_data) == false )
                            return ret_data;
                    }
                }
                if (ret_data != null)
                    return ret_data;
                if (child != null && child.GetType() == typeof(FrameworkElement))
                    FindHeaderControlText(child as FrameworkElement);
            }
        }
        else
        {
            if (element is TextBlock)
            {
                ret_data = (element as TextBlock).Text;
                return ret_data;
            }
            else if (element is ContentControl)
            {
                if ((element as ContentControl).Content != null && 
			((element as ContentControl).Content is string))
                    ret_data = (element as ContentControl).Content.ToString();
                else if ((element as ContentControl).Content is FrameworkElement)
                {
                    ret_data = FindHeaderControlText((element 
			as ContentControl).Content as FrameworkElement);
                }
            }
            return ret_data;
        }
        return ret_data;
    }

    /// <summary>This method searches Header Control with Tag set to 'AccessKey'
    /// </summary>
    /// <param name="element">Header root element</param>
    /// <returns>control text if exist</returns>
    public string FindAccessKeyHeaderControlText(FrameworkElement element)
    {
        int count = VisualTreeHelper.GetChildrenCount(element);
        string ret_data = null;
        if (count > 0)
        {
            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(element, i);
                if (child is FrameworkElement && 
		(child as FrameworkElement).Tag != null &&
                  	(child as FrameworkElement).Tag.GetType() == typeof(string) &&
                  	string.IsNullOrEmpty((child as FrameworkElement).Tag.ToString()) 
								== false &&
	         (child as FrameworkElement).Tag.ToString().ToLower().
							Equals("accesskey"))
                {
                    if (child is ContentControl || child is TextBlock )
                    {
                        if( child is TextBlock )
                        {
                            ret_data = (child as TextBlock).Text;
                            return ret_data;
                        }
                        else if (child is ContentControl)
                        {
                            if ((child as ContentControl).Content != null && 
				((child as ContentControl).Content is string))
                                ret_data = (child as ContentControl).Content.ToString();
                            else if( (child as ContentControl).Content 
						is FrameworkElement )
                            {
                                ret_data = FindAccessKeyHeaderControlText
			     ((child as ContentControl).Content as FrameworkElement);
                            }
                        }
                        return ret_data;
                    }
                }
                if ( ret_data != null )
                    return ret_data;
                if( child != null && child.GetType() == typeof(FrameworkElement) )
                    FindAccessKeyHeaderControlText(child as FrameworkElement);
            }
        }
        return ret_data;
    }

    /// <summary>This method searches Header control that contains string</summary>
    /// <param name="element">Header root element</param>
    /// <returns>Control if exist</returns>
    public FrameworkElement FindAccessKeyHeaderControl(FrameworkElement element)
    {
        int count = VisualTreeHelper.GetChildrenCount(element);
        FrameworkElement ret_data = null;
        if (count > 0)
        {
            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(element, i);
                if (child is FrameworkElement && (child as FrameworkElement).Tag 
								!= null &&
                    (child as FrameworkElement).Tag.GetType() == typeof(string) &&
                    string.IsNullOrEmpty((child 
			as FrameworkElement).Tag.ToString()) == false &&
                    (child as FrameworkElement).Tag.ToString().ToLower().
						Equals("accesskey"))
                {
                    if (child is ContentControl)
                    {
                        ret_data = child as FrameworkElement;
                        return ret_data;
                    }
                }
                if (ret_data != null)
                    return ret_data;
                if (child != null && child.GetType() == typeof(FrameworkElement))
                    FindAccessKeyHeaderControlText(child as FrameworkElement);
            }
        }
        return ret_data;
    }

    /// <summary>Activates MenuItem key stroke ( Underlined access symbol )</summary>
    /// <param name="IsActive">Show/Hide underline rectangle</param>
    internal void ActivateAccessKey( bool IsActive )
    {
        if (_accessKey != null)
            _accessKey.Border.Opacity = IsActive ? 1.0d : 0.0d;
    }

    /// <summary>This method handles mouse enter event and 
    /// sets IsMouseOver property to True</summary>
    /// <param name="e">Mouse event args</param>
    protected override void OnMouseEnter(MouseEventArgs e)
    {
        base.OnMouseEnter(e);
        if (IsEnabled)
            IsMouseOver = true;
    }

    /// <summary>This method handles mouse leave event and sets 
    /// IsMouseOver property to False</summary>
    /// <param name="e">Mouse event args</param>
    protected override void OnMouseLeave(MouseEventArgs e)
    {
        base.OnMouseLeave(e);
        IsMouseOver = false;
        if (IsOpened == false)
        {
            if (TopLevel)
            {
                MenuTracker tracker = Menu.Tracker;
                if (tracker.IsMenuActivated)
                {
                    IsHighlighted = true;
                    return;
                }
            }
            IsHighlighted = false;
        }
    }

    /// <summary>Adds MenuItem</summary>
    /// <param name="item"></param>
    public void Add(MenuItem item)
    {
        Items.Add(item);
    }

    /// <summary>Fires up click event</summary>
    internal void FireClickEvent()
    {
        OnClick(new RoutedEventArgs());
    }

    /// <summary></summary>
    /// <param name="e"></param>
    protected virtual void OnClick(RoutedEventArgs e)
    {
        //check group name - existence is radio like button behaviour
        if( string.IsNullOrEmpty(GroupName) == true )
            IsChecked = !IsChecked;
        else if( IsChecked == false )
            IsChecked = !IsChecked;
        if (Click != null)
            Click(this, e);
    }

    /// <summary>Gets or sets checked state</summary>
    public bool IsChecked
    {
        get { return (bool)GetValue(IsCheckedProperty); }
        set { SetValue(IsCheckedProperty, value); }
    }

    /// <summary>Gets ot sets checked state group</summary>
    public string GroupName
    {
        get { return (string)GetValue(GroupNameProperty); }
        set { SetValue(GroupNameProperty, value); }
    }

    IEnumerable<MenuItem> IItemsControl.InternalItems
    {
        get { return Items; }
    }

    internal ItemAccessKey AccessKey
    {
        get { return _accessKey; }
    }

    internal MenuTracker Tracker
    {
        get { return _menu_tracker; }
    }

    public ObservableCollection<MenuItem> Items
    {
        get { return (ObservableCollection<MenuItem>)GetValue(ItemsProperty); }
    }

    public bool IsMouseOver
    {
        get
        {
            return (bool)this.GetValue(IsMouseOverProperty);
        }
        internal set
        {
            base.SetValue(IsMouseOverProperty, value);
        }
    }

    public object Header
    {
        get { return GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    public MenuKeyGesture KeyGesture
    {
        get { return (MenuKeyGesture)GetValue(KeyGestureProperty); }
        set { SetValue(KeyGestureProperty, value); }
    }

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public bool TopLevel
    {
        get { return (bool)GetValue(TopLevelProperty); }
        set { SetValue(TopLevelProperty, value); }
    }

    public bool IsOpened
    {
        get { return _is_Opended; }
        set { _is_Opended = value; }
    }

    internal void SetHighlightedDirect(bool value)
    {
        _is_highlighted = value;
        if (value)
        {
            if (_PART_Selected != null)
                _PART_Selected.Visibility = Visibility.Visible;
        }
        else
        {
            if (_PART_Selected != null)
                _PART_Selected.Visibility = Visibility.Collapsed;
        }
    }

    public bool IsHighlighted
    {
        get
        {
            return _is_highlighted;
        }
        set
        {
            if ((Header != null && Header.ToString().Equals("-")) || IsEnabled == false)
                return;
            _is_highlighted = value;
            if (value)
            {
                if (IsOpened && TopLevel)
                    return;
                if (_PART_Selected != null)
                    _PART_Selected.Visibility = Visibility.Visible;
            }
            else
            {
                if (IsOpened)
                    return;
                if (_PART_Selected != null)
                    _PART_Selected.Visibility = Visibility.Collapsed;
            }
        }
    }

    public void HidePopup()
    {
        if (IsOpened == false)
            return;
        IsOpened = false;
        _popup.IsOpen = false;
        IsHighlighted = false;
        if (TopLevel)
        {
            if (_PART_Selected != null)
                _PART_Selected.Visibility = Visibility.Collapsed;
            if (_PART_Opened != null)
                _PART_Opened.Visibility = Visibility.Collapsed;
        }
    }

    public void ShowPopup(Point position)
    {
        if ( IsOpened )
            return;
        IsOpened        = true;
        IsHighlighted   = true;

        if (TopLevel == true)
        {
            if (_PART_Selected != null)
                _PART_Selected.Visibility = Visibility.Collapsed;
            if (_PART_Opened != null)
                _PART_Opened.Visibility = Visibility.Visible;
        }

        Items.OfType<MenuItem>().Where(i => i._PART_Selected 
				!= null).ToList().ForEach( item =>
            {
                item._PART_Selected.Visibility = Visibility.Collapsed;
            });

        if (TopLevel)
        {
            _PART_PopupTopRect.Visibility = Visibility.Visible;
            _PART_PopupTopRect.Width = ActualWidth - 2;
        }

        _popup.HorizontalOffset = position.X;
        _popup.VerticalOffset   = position.Y;

        Application.Current.RootVisual.MouseLeftButtonDown += 
		new MouseButtonEventHandler(OnRootVisual_MouseLeftButtonDown);
        _popup.Closed += delegate(object popup_closed, EventArgs popup_args)
        {
            Application.Current.RootVisual.MouseLeftButtonDown -= 
		new MouseButtonEventHandler(OnRootVisual_MouseLeftButtonDown);
        };

        OnPopupOpening(new RoutedEventArgs());
        //lets make some animation.....
        _popup.IsOpen = true;
        Menu.AnimatePopup(_popup);
        OnPopupOpened(new RoutedEventArgs());
    }

    protected void OnPopupOpening(RoutedEventArgs e) {
        if (Opening != null)
            Opening(this, e);
    }
    
    protected void OnPopupOpened(RoutedEventArgs e) {
        if (Opened != null)
            Opened(this, e);
    }

    protected void OnRootVisual_MouseLeftButtonDown
			(object sender, MouseButtonEventArgs e)
    {
        this._menu_tracker.CloseAllChildPopups(this, true);
    }

    public MenuItem ParentItem
    {
        get;
        set;
    }

    public Menu Menu
    {
        get;
        set;
    }

    public FrameworkElement RootElement
    {
        get { return _RootElement; }
    }

    public Rectangle HitTestArea
    {
        get { return _PART_HitTest; }
    }

    public System.Windows.Controls.Primitives.Popup Popup
    {
        get { return _popup; }
    }
    public bool HasKeyGesture
    {
        get { return KeyGesture != null && KeyGesture.Gesture != Key.None; }
    }

    public bool HasOpenedChild
    {
        get
        {
            return (from child in Items.Cast<MenuItem>() 
		where child.IsOpened select child).Count() > 0;
        }
    }

    public MenuItem OpenedChild 
    {
        get
        {
            return (from child in Items.Cast<MenuItem>() 
		where child.IsOpened select child).First();
        }
    }

    public MenuItem GetLastOpenedChild()
    {
        MenuItem ret_val = null;
        if (HasOpenedChild)
            GetLastOpenedChild(OpenedChild, ref ret_val);
        return ret_val;
    }

    public MenuItem GetHighlightedChild()
    {
        IEnumerable<MenuItem> found = 
	(from item in Items.Cast<MenuItem>() where item.IsHighlighted select item);
        if (found.Count() > 0)
            return found.First();
        return null;
    }

    protected void GetLastOpenedChild(MenuItem item, ref MenuItem LastOpened)
    {
        if (item.IsOpened)
            LastOpened = item;
        if (item.HasOpenedChild)
            item.GetLastOpenedChild(item.OpenedChild, ref LastOpened);
    }

    public bool IsSeparator
    {
        get { return Header != null && Header.ToString().Equals("-"); }
    }
}

此类和 Menu 类的样式方法相同,都在类构造函数中完成

public MenuItem()
{
    TopLevel = false;
    SetValue(ItemsProperty, new ObservableCollection<MenuItem>());
    Items.CollectionChanged += 
	new System.Collections.Specialized.NotifyCollectionChangedEventHandler
	(OnItems_CollectionChanged);
    base.DefaultStyleKey = typeof(MenuItem);
}

MenuItem 的样式

<local:MenuKeyGestureValueConverter x:Key="GestureConverter"/>
<local:BooleanToVisibilityConverter x:Key="BooleanVisibilityConveter"/>

<!--MENU ITEM STYLE-->
<Style TargetType="local:MenuItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MenuItem">
                <Grid Opacity="1.0" x:Name="RootElement" 
		Visibility="Visible" Height="Auto" Width="Auto" >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition MinWidth="19" Width="26"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="26"/>
                    </Grid.ColumnDefinitions>
                    <Rectangle Margin="2,0,0,0" Grid.Column="0" 
			Visibility="Collapsed" x:Name="PART_LeftBar">
                        <Rectangle.Fill>
                            <LinearGradientBrush  EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FFE9E8E8" Offset="0"/>
                                <GradientStop Color="#FFE9E8E8" Offset="1"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <Border Grid.ColumnSpan="4" Visibility="Collapsed" 
			Background="#FFF5F5F5" BorderBrush="#FF868686" 
			x:Name="PART_Opened" BorderThickness="1" 
			CornerRadius="2,2,2,0"/>
                    <Grid  x:Name="PART_Selected" Margin="0,0,0,0" 
			Grid.ColumnSpan="4" Visibility="Collapsed">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="0.5*"/>
                            <RowDefinition Height="0.5*"/>
                        </Grid.RowDefinitions>
                        <Rectangle Stroke="#FF9FBBE3" StrokeThickness="0.05" 
			RadiusX="2" RadiusY="2" Grid.RowSpan="2" x:Name="rectangle1">
                            <Rectangle.Fill>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFDAE4EF" Offset="0"/>
                                    <GradientStop Color="#FFECEBC0" Offset="1"/>
                                </LinearGradientBrush>
                            </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle Grid.RowSpan="2" Margin="2,2,2,2" 
				RadiusX="2" RadiusY="2" x:Name="rectangle2">
                            <Rectangle.Fill>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFFFFFF9" Offset="0"/>
                                    <GradientStop Color="#FFFFF7B9" Offset="1"/>
                                </LinearGradientBrush>
                            </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle Margin="3,3,3,0" x:Name="rectangle3">
                            <Rectangle.Fill>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFFFF9D9" Offset="0"/>
                                    <GradientStop Color="#FFFFEEB2" Offset="1"/>
                                </LinearGradientBrush>
                            </Rectangle.Fill>
                        </Rectangle>
                        <Rectangle Margin="3,0,3,3" Grid.Row="1" x:Name="rectangle4">
                            <Rectangle.Fill>
                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                    <GradientStop Color="#FFFFD66B" Offset="0"/>
                                    <GradientStop Color="#FFFFE086" Offset="1"/>
                                </LinearGradientBrush>
                            </Rectangle.Fill>
                        </Rectangle>
                    </Grid>
                    <Border Grid.ColumnSpan="4" Visibility="Visible" 
			Background="#FF808080" BorderBrush="#FF868686" 
			x:Name="PART_Separator" BorderThickness="1" 
			CornerRadius="0,0,0,0" Width="Auto" Height="1" 
			Margin="30,0,3,0"/>
                    <Grid Grid.Column="1" Width="Auto" Height="Auto">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="0.5*"/>
                            <ColumnDefinition Width="0.5*"/>
                        </Grid.ColumnDefinitions>
                        <ContentControl Grid.Column="0" 
			VerticalAlignment="Center" HorizontalAlignment="Center" 
			x:Name="PART_Header" Content="{TemplateBinding Header}"/>
                        <TextBlock Grid.Column="1" Margin="10,0,-12,0" 
			VerticalAlignment="Center" HorizontalAlignment="Right"
                            Visibility="{Binding 
                              Path=HasKeyGesture, 
                              RelativeSource={RelativeSource TemplatedParent}, 
                              Converter={StaticResource BooleanVisibilityConveter}}"
                            Text="{Binding 
                              Path=KeyGesture, 
                              RelativeSource={RelativeSource TemplatedParent}, 
                              Converter={StaticResource GestureConverter}}" 
				x:Name="PART_GestureString" TextWrapping="NoWrap">
                            </TextBlock>
                    </Grid>
                    <ContentControl HorizontalAlignment="Center" 
			Grid.Column="3" RenderTransformOrigin="0.5,0" 
			Margin="6,9,2,0" VerticalAlignment="Center" 
			Width="9" Height="9" x:Name="PART_SubMenuGrip" 
			Visibility="Collapsed">
                        <ContentControl.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform/>
                                <SkewTransform/>
                                <RotateTransform Angle="45"/>
                                <TranslateTransform/>
                            </TransformGroup>
                        </ContentControl.RenderTransform>
                        <ContentControl.Content>
                            <Canvas Width="9" Height="9" 
				HorizontalAlignment="Center" 
				VerticalAlignment="Center">
                                <Polygon Points="0,0 6,0 5,5 0,0"
                                         Stroke="{x:Null}" StrokeThickness="2" 
					x:Name="polygon">
                                    <Polygon.Fill>
                                        <RadialGradientBrush>
                                            <GradientStop Color="#FF000000"/>
                                            <GradientStop Color="#FF000000" Offset="1"/>
                                        </RadialGradientBrush>
                                    </Polygon.Fill>
                                </Polygon>
                            </Canvas>
                        </ContentControl.Content>
                    </ContentControl>
                    <Rectangle Grid.ColumnSpan="3" Fill="White" 
				Opacity="0.01" x:Name="PART_HitTest"/>
                    <Popup x:Name="PART_Popup">
                        <Grid>
                            <Border BorderThickness="1,1,1,1" 
				Background="#FFF5F5F5" BorderBrush="#FF868686" 
				CornerRadius="0,2,2,2"/>
                            <ItemsControl ItemsSource="{TemplateBinding Items}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel x:Name="PART_PopupItemsHost" 
					Orientation="Vertical" Margin="2,2,2,2"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                            <Rectangle Height="2" Width="80" 
				VerticalAlignment="Top" HorizontalAlignment="Left" 
				Margin="1,-1,0,0" Fill="#FFF5F5F5" 
				x:Name="PART_PopupTopRect" Visibility="Collapsed"/>
                        </Grid>
                    </Popup>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Foreground" Value="Black"/>
</Style>

上述两个类都有一个 MenuTracker 对象实例,用于控制 MenuItems 的弹出行为和键盘导航。

<MenuTracker Class>

internal class MenuTracker
    {
        private FrameworkElement _root;
        private bool _tracking;
        private MenuItem _highlighted = null;
        private Menu _menu;
        private IItemsControl _owner;
        private bool _IsRoot = false;
        private bool _menu_was_activated = false;
        private bool _menu_keyboard_navigation = false;
        private bool _last_keyup_was_gesture;
        private bool _do_not_process_next_keyup = false;

        public MenuTracker(Menu menu, IItemsControl owner, 
				FrameworkElement root, bool IsRoot)
        {
            _IsRoot = IsRoot;
            _root = root;
            _owner = owner;
            _menu = menu;
            HookMenu();
        }

        private void HookMenu()
        {
            if (_root != null)
            {
                _root.MouseEnter += new MouseEventHandler(OnMenu_MouseEnter);
                _root.MouseLeave += new MouseEventHandler(OnMenu_MouseLeave);
                _root.MouseMove += new MouseEventHandler(OnMenu_MouseMove);
                _root.MouseLeftButtonDown += 
			new MouseButtonEventHandler(OnMenu_MouseLeftButtonDown);
                
                if (_IsRoot)
                {
                    _root.KeyUp += new KeyEventHandler(OnRoot_KeyUp);
                    _root.KeyDown += new KeyEventHandler(OnRoot_KeyDown);
                }
            }
        }

        protected void OnRoot_KeyDown(object sender, KeyEventArgs e)
        {
            if (_last_keyup_was_gesture)
            {
                _last_keyup_was_gesture = false;
                return;
            }

            if (_IsRoot == true && _menu.ActivationKeyGesture.Gesture == e.Key)
            {
                this._menu_keyboard_navigation = true;
                _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                  {
                      ActivateAccessKey(item, true);
                  });
            }
        }

        protected void OnRoot_KeyUp(object sender, KeyEventArgs e)
        {
            MenuItem g_item = null;
            ModifierKeys mod = Keyboard.Modifiers;
            _owner.InternalItems.ToList().ForEach(item =>
              {
                  if (g_item != null)
                      return;
                  g_item = FindItemForGesture(item, e.Key, mod);
              });
            if (g_item != null)
            {
                _last_keyup_was_gesture = true;
                g_item.FireClickEvent();
                _menu.ClosePopups();
                this._menu.Items.Cast<MenuItem>().ToList().ForEach(i =>
                {
                    ActivateAccessKey(i, false);
                });
                _highlighted = null;
                return;
            }
            else
            {
                if ( mod != ModifierKeys.None && _menu_keyboard_navigation == false )
                {
                    _last_keyup_was_gesture = true;
                    return;
                }
                else if (_IsRoot == true && IsMenuActivated == 
		false && _menu_keyboard_navigation && LookUpAccessKeyItem
		(_menu.Items.Cast<MenuItem>(), false, e.PlatformKeyCode))
                {
                    //this is then menu is activated = false;
                    _do_not_process_next_keyup = true;
                    _tracking = true;
                    if (_highlighted != null)
                    {
                        if (_highlighted.IsOpened)
                        {
                            this.CloseAllChildPopups(_highlighted, true);
                            if (_highlighted.IsMouseOver == false)
                            {
                                _highlighted.IsHighlighted = false;
                                _highlighted = null;
                            }
                            else
                                _highlighted.SetHighlightedDirect(true);
                            _menu_keyboard_navigation = false;
                            _menu_was_activated = false;
                            _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                            {
                                ActivateAccessKey(item, false);
                            });
                        }
                        else
                        {
                            if (_menu_was_activated)
                            {
                                _highlighted.SetHighlightedDirect(false);
                                _menu_keyboard_navigation = false;
                                _menu_was_activated = false;
                                _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                                {
                                    item.ActivateAccessKey(false);
                                });
                            }
                            else
                            {
                                _menu_was_activated = true;
                                this.CloseAllChildPopups(_highlighted, true);
                                _highlighted.IsHighlighted = false;
                                _highlighted = _menu.Items.Count() > 0 
				? _menu.Items.Cast<MenuItem>().First() : null;
                                if (_highlighted != null)
                                {
                                    _highlighted.IsHighlighted = true;
                                }
                            }
                        }
                    }
                    else
                    {
                        _highlighted = _menu.Items.Count() > 0 ? 
			_menu.Items.Cast<MenuItem>().First() : null;
                        if (_highlighted != null)
                        {
                            _highlighted.IsHighlighted = true;
                            _menu_was_activated = true;
                        }
                    }
                }
            }

            //Find an access key for highlighted menu item...
            if (_highlighted != null && ( IsMenuActivated || 
			_menu._menu_tracker.IsMenuActivated ) )
            {
                //this would mean that we have menu activated, 
	       //so we search for an access key for an item in
                //inside of opened menu item.
                if ( _highlighted.IsOpened == false )
                {
                    FindAccessKeyItem(_menu.Items.Cast<MenuItem>(), 
					false, e.PlatformKeyCode);
                }
                else
                {
                    IEnumerable<MenuItem> items = 
			_highlighted.GetLastOpenedChild() != null ?
                        _highlighted.GetLastOpenedChild().Items : _highlighted.Items;
                    FindAccessKeyItem(items, true, e.PlatformKeyCode);
                }
            }
            //***********************************************

            //THIS SECTION IS FOR MENU KEY NAVIGATION....

            if (_last_keyup_was_gesture)
            {
                _last_keyup_was_gesture = false;
                _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                {
                    item.ActivateAccessKey(false);
                });
                return;
            }

            if (_IsRoot == true && IsMenuKeyPressed(e.Key))
            {
                if (_do_not_process_next_keyup)
                {
                    _do_not_process_next_keyup = false;
                    return;
                }
                //this is then menu is activated = false;
                _tracking = true;
                if (_highlighted != null)
                {
                    if (_highlighted.IsOpened)
                    {
                        this.CloseAllChildPopups(_highlighted, true);
                        if (_highlighted.IsMouseOver == false)
                        {
                            _highlighted.IsHighlighted = false;
                            _highlighted = null;
                        }
                        else
                            _highlighted.SetHighlightedDirect(true);
                        _menu_keyboard_navigation = false;
                        _menu_was_activated = false;
                        _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                        {
                            ActivateAccessKey(item, false);
                        });
                    }
                    else
                    {
                        if (_menu_was_activated)
                        {
                            _highlighted.SetHighlightedDirect(false);
                            _menu_keyboard_navigation = false;
                            _menu_was_activated = false;
                            _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                            {
                                item.ActivateAccessKey(false);
                            });
                        }
                        else
                        {
                            _menu_was_activated = true;
                            this.CloseAllChildPopups(_highlighted, true);
                            _highlighted.IsHighlighted = false;
                            _highlighted = _menu.Items.Count() > 0 ? 
				_menu.Items.Cast<MenuItem>().First() : null;
                            if (_highlighted != null)
                            {
                                _highlighted.IsHighlighted = true;
                            }
                        }
                    }
                }
                else
                {
                    _highlighted = _menu.Items.Count() > 0 ? 
			_menu.Items.Cast<MenuItem>().First() : null;
                    if (_highlighted != null)
                    {
                        _highlighted.IsHighlighted = true;
                        _menu_was_activated = true;
                    }
                }
            }

            if (_IsRoot && e.Key == Key.Escape && _menu_was_activated)
            {
                if (_highlighted != null)
                {
                    if (_highlighted.IsOpened)
                    {
                        this.CloseAllChildPopups(_highlighted, true);
                    }
                    if (_highlighted.IsMouseOver == false)
                    {
                        _highlighted.IsHighlighted = false;
                        _highlighted = null;
                    }
                    _menu_keyboard_navigation = false;
                    _menu_was_activated = false;
                    _menu.Items.Cast<MenuItem>().ToList().ForEach(item =>
                    {
                        item.ActivateAccessKey(false);
                    });
                }
            }

            if (_IsRoot && _menu_was_activated || _menu.Tracker.IsMenuActivated)
            {
                NavigateMenu(e.Key);
                if (e.Key == Key.Enter)
                {
                    if (_highlighted != null)
                    {
                        MenuItem last = _highlighted.GetLastOpenedChild();
                        MenuItem active = last != null ? 
		      last.GetHighlightedChild() : _highlighted.GetHighlightedChild();
                        if (active != null)
                        {
                            if (active.Items.Count == 0)
                            {
                                active.FireClickEvent();
                                CloseAllChildPopups(active, true);
                                _menu_keyboard_navigation = false;
                                _menu_was_activated = false;
                                this._menu.Items.Cast<MenuItem>().ToList().ForEach(i =>
                                {
                                    ActivateAccessKey(i, false);
                                });
                                _highlighted = null;
                            }
                            else
                            {
                                ShowPopup(active);
                                active.Items.Cast<MenuItem>().First()
							.IsHighlighted = true;
                            }
                        }
                    }
                }
            }
        }

        protected bool LookUpAccessKeyItem(IEnumerable<MenuItem> items, 
					bool IsRootOpen, int KeyCode)
        {
            IEnumerable<MenuItem> found = (from i in items.Cast<MenuItem>()
                                           where i.AccessKey != null && 
					string.IsNullOrEmpty(i.AccessKey.Key) 
					== false &&
                                           i.AccessKey.Key.Length > 0 &&
                                           i.AccessKey.Key.ToLower() == 
					((char)KeyCode).ToString().ToLower()
                                           select i);
            return found.Count() > 0;
        }

        protected void FindAccessKeyItem(IEnumerable<MenuItem> items, 
					bool IsRootOpen, int KeyCode)
        {
            IEnumerable<MenuItem> found = (from i in items.Cast<MenuItem>()
                                           where i.AccessKey != null && 
					string.IsNullOrEmpty
					(i.AccessKey.Key) == false &&
                                           i.AccessKey.Key.Length > 0 &&
                                           i.AccessKey.Key.ToLower() == 
					((char)KeyCode).ToString().ToLower()
                                           select i);
            if (found.Count() > 0)
            {
                if (found.First().Items.Count == 0)
                {
                    //click
                    _last_keyup_was_gesture = true;
                    found.First().FireClickEvent();
                    _menu.ClosePopups();
                    this._menu.Items.Cast<MenuItem>().ToList().ForEach(i =>
                    {
                        ActivateAccessKey(i, false);
                    });
                    this._menu._menu_tracker._menu_was_activated = false;
                    this._menu._menu_tracker._highlighted = null;
                    _highlighted = null;
                    return;
                }
                else
                {
                    _menu_keyboard_navigation = true;
                    if (found.First().ParentItem != null && 
			found.First().ParentItem.GetHighlightedChild() != null )
                        found.First().ParentItem.GetHighlightedChild().
						IsHighlighted = false;
                    
                    found.First().IsHighlighted = true;

                    //found.First()._menu_tracker._highlighted = found.First();
                    
                    //Navigate to an item we need
                    if (IsRootOpen == false && _highlighted != found.First())
                    {
                        while(_highlighted != found.First())
                            NavigateMenu(Key.Right);
                    }

                    _menu_was_activated = true;
                    //Dropdown item
                    NavigateMenu(IsRootOpen ? Key.Right : Key.Down );
                    //show access key stroke
                    this._menu.Items.Cast<MenuItem>().ToList().ForEach(i =>
                    {
                        ActivateAccessKey(i, true);
                    });
                    return;
                }
            }
        }

        /// <summary>This method implements code for menu keyboard navigation</summary>
        /// <param name="key">The key that was fired by key up event</param>
        protected void NavigateMenu(Key key)
        {
            if (_highlighted != null)
            {
                List<MenuItem> root_items = null;
                MenuItem fh;
                int index = 0;

                switch (key)
                {
                    case Key.Left:
                        if (_highlighted.IsOpened == false)
                        {
                            //find next root item
                            root_items = _menu.Items.Cast<MenuItem>().ToList();
                            index = root_items.IndexOf(_highlighted);
                            if (index == 0)
                                index = root_items.Count - 1;
                            else
                                index = index - 1 >= 0 && index != 0 ? index - 1 : index;
                            _highlighted.IsHighlighted = false;
                            _highlighted = root_items[index];
                            _highlighted.IsHighlighted = true;
                        }
                        else
                        {
                            if (_highlighted.TopLevel)
                            {
                                if (_highlighted.HasOpenedChild)
                                {
                                    MenuItem last = _highlighted.GetLastOpenedChild();
                                    if (last != null)
                                    {
                                        last.HidePopup();
                                        last.IsHighlighted = true;
                                        _menu_keyboard_navigation = true;
                                    }
                                }
                                else//No opened child...
                                {
                                    //find next root item
                                    root_items = _menu.Items.Cast<MenuItem>().ToList();
                                    index = root_items.IndexOf(_highlighted);
                                    if (index == 0)
                                        index = root_items.Count - 1;
                                    else
                                        index = index - 1 >= 0 && 
					index != 0 ? index - 1 : index;
                                    _highlighted.IsHighlighted = false;
                                    CloseAllChildPopups(_highlighted, true);
                                    _highlighted = root_items[index];
                                    _highlighted.IsHighlighted = true;
                                    ShowPopup(_highlighted);
                                    if (_highlighted.Items.Count > 0)
                                    {
                                        _highlighted.Items.Cast
					<MenuItem>().First().IsHighlighted = true;
                                    }
                                }
                            }
                        }
                        break;
                    case Key.Right:
                        if (_highlighted.IsOpened == false)
                        {
                            //find next root item
                            root_items = _menu.Items.Cast<MenuItem>().ToList();
                            index = root_items.IndexOf(_highlighted);
                            if (index == root_items.Count - 1)
                                index = 0;
                            else
                                index = index + 1 
				<= root_items.Count ? index + 1 : index;
                            _highlighted.IsHighlighted = false;
                            _highlighted = root_items[index];
                            _highlighted.IsHighlighted = true;
                        }
                        else
                        {
                            if (_highlighted.TopLevel)
                            {
                                if (_menu_keyboard_navigation == false)
                                {
                                    //find next root item
                                    root_items = _menu.Items.Cast<MenuItem>().ToList();
                                    index = root_items.IndexOf(_highlighted);
                                    if (index == root_items.Count - 1)
                                        index = 0;
                                    else
                                        index = index + 1 
					<= root_items.Count ? index + 1 : index;
                                    _highlighted.IsHighlighted = false;
                                    CloseAllChildPopups(_highlighted, true);
                                    _highlighted = root_items[index];
                                    _highlighted.IsHighlighted = true;
                                    ShowPopup(_highlighted);
                                    if (_highlighted.Items.Count > 0)
                                        _highlighted.Items.Cast
					<MenuItem>().First().IsHighlighted = true;
                                }
                                else if (_menu_keyboard_navigation)
                                {
                                    MenuItem last = _highlighted.GetLastOpenedChild();
                                    MenuItem highlighted = null;
                                    if (last != null)
                                        highlighted = last.GetHighlightedChild();
                                    else
                                        highlighted = _highlighted.GetHighlightedChild();
                                    if (highlighted != null)
                                    {
                                        if (highlighted.Items.Count == 0)
                                        {
                                            //find next root item
                                            root_items = _menu.Items.Cast
							<MenuItem>().ToList();
                                            index = root_items.IndexOf(_highlighted);
                                            if (index == root_items.Count - 1)
                                                index = 0;
                                            else
                                                index = index + 1 
					   <= root_items.Count ? index + 1 : index;
                                            _highlighted.IsHighlighted = false;
                                            CloseAllChildPopups(_highlighted, true);
                                            _highlighted = root_items[index];
                                            _highlighted.IsHighlighted = true;
                                            ShowPopup(_highlighted);
                                            if (_highlighted.Items.Count > 0)
                                                _highlighted.Items.Cast
					<MenuItem>().First().IsHighlighted = true;
                                        }
                                        else
                                        {
                                            ShowPopup(highlighted);
                                            if (highlighted.Items.Count > 0)
                                            {
                                                highlighted = 
					highlighted.Items.Cast<MenuItem>().First();
                                                highlighted.IsHighlighted = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        break;

                    case Key.Up:
                        fh = null;
                        //find next root item
                        if (_highlighted.HasOpenedChild == false)
                            root_items = _highlighted.Items.Cast<MenuItem>().ToList();
                        else
                        {
                            fh = _highlighted.GetLastOpenedChild();
                            root_items = fh.Items.Cast<MenuItem>().ToList();
                        }
                        fh = fh == null ? _highlighted.GetHighlightedChild() : 
						fh.GetHighlightedChild();
                    again_up://this is for a separator to be passed off
                        index = fh != null ? root_items.IndexOf(fh) : 0;
                    if (fh != null)
                    {
                        fh.IsHighlighted = false;
                        CloseAllChildPopups(fh, false);
                    }
                    if (index == 0)
                        index = root_items.Count - 1;
                    else if (fh != null)
                        index = index - 1 >= 0 && index != 0 ? index - 1 : index;
                    fh = root_items[index];
                    if (fh.IsSeparator)
                        goto again_up;
                    fh.IsHighlighted = true;
                    _menu_keyboard_navigation = true;
                    if (_highlighted.IsOpened == false)
                        ShowPopup(_highlighted);
                    break;

                case Key.Down:
                    //find next root item
                    fh = null;
                    if (_highlighted.HasOpenedChild == false)
                        root_items = _highlighted.Items.Cast<MenuItem>().ToList();
                    else
                    {
                        fh = _highlighted.GetLastOpenedChild();
                        root_items = fh.Items.Cast<MenuItem>().ToList();
                    }
                    fh = fh == null ? _highlighted.GetHighlightedChild() : 
						fh.GetHighlightedChild();
                again_down:
                    index = fh != null ? root_items.IndexOf(fh) : 0;
                    if (fh != null)
                    {
                        fh.IsHighlighted = false;
                        CloseAllChildPopups(fh, false);
                    }
                    if (index == root_items.Count - 1)
                        index = 0;
                    else if (fh != null)
                        index = index + 1 <= root_items.Count ? index + 1 : index;
                    fh = root_items[index];
                    if (fh.IsSeparator)
                        goto again_down;
                    if (_highlighted.IsOpened == false)
                        ShowPopup(_highlighted);
                    _menu_keyboard_navigation = true;
                    fh.IsHighlighted = true;
                    _highlighted.IsHighlighted = true;
                    break;
                }
            }
        }

        /// <summary>This method determines if a Menu access key was pressed</summary>
        /// <param name="key">The key that was fired by key up event</param>
        /// <returns>True - the menu key was pressed ( default is Ctrl )</returns>
        protected bool IsMenuKeyPressed(Key key)
        {
            ModifierKeys mod = Keyboard.Modifiers;
            if (_menu.ActivationKeyGesture != null)
            {
                if ((_menu.ActivationKeyGesture.Gesture == key && 
			mod == _menu.ActivationKeyGesture.ModifierKey))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>This method searches for menu item with GeyGesture 
        /// that match the passed parameters</summary>
        /// <param name="item">Root Item</param>
        /// <param name="key">The key that was pressed</param>
        /// <param name="mod">The modifier key that was pressed</param>
        /// <returns>MenuItem object if found, otherwise null</returns>
        protected MenuItem FindItemForGesture(MenuItem item, Key key, ModifierKeys mod)
        {
            if (item.KeyGesture != null)
            {
                if (item.KeyGesture.Gesture == key && item.KeyGesture.ModifierKey == mod)
                    return item;
            }
            MenuItem g_item = null;
            item.Items.Cast<MenuItem>().ToList().ForEach(citem =>
              {
                  if (g_item != null)
                      return;
                  g_item = FindItemForGesture(citem, key, mod);
              });
            return g_item;
        }

        /// <summary>This method handles Left Mouse Button down event 
        /// fired by root object of this instance</summary>
        /// <param name="sender">Message sender</param>
        /// <param name="e">Message event args</param>
        protected void OnMenu_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (_IsRoot && _highlighted != null)
            {
                FrameworkElement element = _root;
                MenuItem item = _highlighted;
                Point screen_item_location = 
		item.TransformToVisual(element).Transform(new Point(0, 0));
                Point screen_mouse_location = item.TransformToVisual(element).Transform(
                    e.GetPosition(item.HitTestArea));
                Rect bounds = new Rect(screen_item_location.X, 
		screen_item_location.Y, (item as MenuItem).HitTestArea.ActualWidth, 
		(item as MenuItem).HitTestArea.ActualHeight);
                bool is_item_mouse_over = bounds.Contains(screen_mouse_location);
                if (is_item_mouse_over == false)
                {
                    CloseAllChildPopups(_highlighted, true);
                    this._menu.Items.Cast<MenuItem>().ToList().ForEach(i =>
                    {
                        ActivateAccessKey(i, false);
                    });
                    this._menu._menu_tracker._menu_was_activated = false;
                    this._menu._menu_tracker._highlighted = null;
                    _highlighted = null;
                    return;
                }
            }

            if (_highlighted != null)
            {
                if (_highlighted.Items.Count > 0)
                {
                    ShowPopup(_highlighted);
                }
                else if (_highlighted.Items.Count == 0)
                {
                    CloseAllChildPopups(_highlighted, true);
                    _highlighted.FireClickEvent();
                    _tracking = false;
                    _highlighted.IsHighlighted = false;
                    _menu_was_activated = false;
                    //we have to deactivate all parent menu items...
                    if (_highlighted.ParentItem != null)
                    {
                        MenuItem tmp = _highlighted.ParentItem;
                        while (tmp != null)
                        {
                            tmp.IsHighlighted = false;
                            tmp._menu_tracker._menu_was_activated = false;
                            tmp._menu_tracker._highlighted = null;
                            tmp = tmp.ParentItem;
                        }
                    }

                    this._menu.Items.Cast<MenuItem>().ToList().ForEach(i =>
                        {
                            ActivateAccessKey(i, false);
                        });

                    this._menu._menu_tracker._menu_was_activated = false;
                    this._menu._menu_tracker._highlighted = null;
                    _highlighted = null;
                }
            }
        }

        /// <summary>This method enables visual keystroke for menu item if available
        /// </summary>
        /// <param name="item">The item for keystroke to be enabled</param>
        /// <param name="Activate">True-activate(show), False - deactivate(hide)</param>
        protected void ActivateAccessKey(MenuItem item, bool Activate)
        {
            item.ActivateAccessKey(Activate);

            item.Items.Cast<MenuItem>().ToList().ForEach(i =>
            {
                ActivateAccessKey(i, Activate);
            });
        }

        /// <summary>Hides menu item popup</summary>
        /// <param name="item">An item that contains an open popup</param>
        protected void HidePopup(MenuItem item)
        {
            if (item.IsOpened == false)
                return;
            item.HidePopup();
        }

        /// <summary>Shows menu item popup if available</summary>
        /// <param name="item">An item that contains hidden popup</param>
        protected void ShowPopup(MenuItem item)
        {
            if (item.IsOpened || item.Items.Count == 0)
                return;
            
            if (item.TopLevel)
            {
                _menu_was_activated = true;
                FrameworkElement element = _root.GetRoot();
                Point screen_item_location = 
		item.TransformToVisual(element).Transform(new Point(0, 0));
                Rect bounds = new Rect(screen_item_location.X, 
		screen_item_location.Y, item.HitTestArea.ActualWidth, 
		item.HitTestArea.ActualHeight);
                item.ShowPopup(new Point(0, bounds.Height - 3));
                item.Popup.UpdateLayout();
                if ((bounds.Y + bounds.Height - 1 + 
		item.Popup.ActualHeight) > element.Height)
                {
                    item.HidePopup();
                    bounds.Y -= (item.Popup.ActualHeight + bounds.Height);
                    item.ShowPopup(new Point(bounds.X, bounds.Y));
                }
            }
            else
            {
                item.ParentItem.Items.Cast<MenuItem>().ToList().ForEach(ch => 
			{ ch.HidePopup(); });
                FrameworkElement element = _root.GetRoot();
                Point screen_item_location = 
		item.TransformToVisual(element).Transform(new Point(0, 0));
                Rect bounds = new Rect(screen_item_location.X, 
		screen_item_location.Y, item.HitTestArea.ActualWidth, 
		item.HitTestArea.ActualHeight);
                item.ShowPopup(new Point(/*bounds.X + */bounds.Width - 1, 
							1/*bounds.Y*/));
            }
        }

        protected Rect GetPopupBounds(System.Windows.Controls.Primitives.Popup popup)
        {
            Rect bounds = new Rect(0, 0, 
		(popup.Child as FrameworkElement).ActualWidth, 
		(popup.Child as FrameworkElement).ActualHeight);
            return bounds;
        }

        public void CloseAllChildPopups(MenuItem item, bool IncludeParent)
        {
            item.IsHighlighted = false;
            if (IncludeParent && item.ParentItem != null)
            {
                MenuItem tmp = item.ParentItem;
                while (tmp != null)
                {
                    tmp.IsHighlighted = false;
                    tmp.HidePopup();
                    tmp = tmp.ParentItem;
                }
            }
            item.Items.OfType<MenuItem>().ToList().ForEach(nitem =>
                {
                    nitem.IsHighlighted = false;
                    nitem.HidePopup();
                    CloseAllChildPopups(nitem, IncludeParent);
                });
        }

        protected virtual void OnMenu_MouseMove(object sender, MouseEventArgs e)
        {
            if (_tracking)
            {
                List<MenuItem> found = new List<MenuItem>
		(_owner.InternalItems.Where(item => item.IsMouseOver == false));

                found.ForEach(item =>
                {
                    if (_menu_was_activated && item != _highlighted)
                        item.IsHighlighted = false;
                });

                (from item in _owner.InternalItems where item.IsMouseOver == 
			true select item).ToList().ForEach(item =>
                  {
                      if (_highlighted != null && _highlighted != item)
                      {
                          bool _top_level_opened = false;
                          if (item.TopLevel == true)
                          {
                              _top_level_opened = _highlighted.IsOpened;
                              CloseAllChildPopups(_highlighted, false);
                          }
                          HidePopup(_highlighted);
                          _highlighted = item;
                          item.IsHighlighted = true;
                          if (_top_level_opened)
                              ShowPopup(_highlighted);
                          return;
                      }

                      //a keyboard navigated child could be selected & highlighted
                      if (_menu_keyboard_navigation && item.ParentItem 
			!= null && item.ParentItem.GetHighlightedChild() != null)
                      {
                          MenuItem hitem = item.ParentItem.GetHighlightedChild();
                          CloseAllChildPopups(hitem, false);
                          hitem.IsHighlighted = false;
                          hitem.HidePopup();
                      }

                      _highlighted = item;
                      item.IsHighlighted = true;

                      if (item.TopLevel == false)
                      {
                          ShowPopup(_highlighted);
                      }
                  });
                if (_highlighted != null && _menu_was_activated)
                {
                    _highlighted.IsHighlighted = true;
                }
            }
        }

        protected virtual void OnMenu_MouseEnter(object sender, MouseEventArgs e){
            _tracking = true;
        }

        protected virtual void OnMenu_MouseLeave(object sender, MouseEventArgs e) {
        }

        public bool IsMenuActivated
        {
            get { return _menu_was_activated; }
        }
    }

此对象的主要技巧是,当执行 Menu 类的构造函数代码时,它会运行以下代码来创建 MenuTracker 类的实例:

Loaded += delegate(object sender, RoutedEventArgs e)
{
  _menu_tracker = new MenuTracker(this, this, this.GetRoot(), true);
};

MenuTracker 类构造函数的第三个参数是根元素,它将捕获并处理鼠标事件,例如 MouseEnterMouseMoveMouseDown。这就是根菜单项被填充和高亮显示的方式。控制子菜单项弹出行为也使用相同的方式。每个 MenuItem 类实例都包含一个 MenuTracker 实例,而前面提到的参数是特定 MenuItemPopup 对象。

鼠标事件处理程序在“HookMenu()”方法中挂钩。

private void HookMenu()
{
    if (_root != null)
    {
        _root.MouseEnter += new MouseEventHandler(OnMenu_MouseEnter);
        _root.MouseLeave += new MouseEventHandler(OnMenu_MouseLeave);
        _root.MouseMove += new MouseEventHandler(OnMenu_MouseMove);
        _root.MouseLeftButtonDown += new MouseButtonEventHandler
					(OnMenu_MouseLeftButtonDown);
        
        if (_IsRoot)
        {
            _root.KeyUp += new KeyEventHandler(OnRoot_KeyUp);
            _root.KeyDown += new KeyEventHandler(OnRoot_KeyDown);
        }
    }
}

代码使用示例

<UserControl x:Class="DropDownMenu.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local2="clr-namespace:System.Windows.Controls;assembly=ToolStrips"
    xmlns:local="clr-namespace:System.Windows.Controls;assembly=DropDownMenu"
    xmlns:s="clr-namespace:System;assembly=mscorlib"
    Width="Auto" Height="Auto">
    <Grid Background="Gray">
        <Border  
          Width="600" 
          Height="400" 
          VerticalAlignment="Center" 
          HorizontalAlignment="Center"          
          BorderBrush="#FF084C64" 
          BorderThickness="3,3,3,3" 
          CornerRadius="3,3,3,3" >
            <Grid  x:Name="LayoutRoot" Background="#FF75CEEE">
                <!--GRID ROW DEFENITIONS-->
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
        <!--MAIN SECTION-->
                <local:Menu>
                    <local:MenuItem Header="_File">
                        <local:MenuItem Header="_New">
                            <local:MenuItem Click="OnMenuClick">
                                <local:MenuItem.Header>
                                    <StackPanel Margin="-24,0,0,0" 
					Orientation="Horizontal">
                                        <Image VerticalAlignment="Center" 
					Width="16" Height="16" 
				       Source="Resources/Images/NewDocumentHS.png"/>
                                        <ContentControl Tag="AccessKey" 
					Margin="8,0,0,0" Content="_Project" 
					VerticalAlignment="Center"/>
                                    </StackPanel>
                                </local:MenuItem.Header>
                            </local:MenuItem>
                            <local:MenuItem Click="OnMenuClick" Header="Web Site"/>
                            <local:MenuItem Click="OnMenuClick" Header="File"/>
                            <local:MenuItem Click="OnMenuClick" 
				Header="Project From Existing Code"/>
                        </local:MenuItem>
                        <local:MenuItem Header="_Open">
                            <local:MenuItem KeyGesture="Shift+O" Click="OnMenuClick">
                                <local:MenuItem.Header>
                                    <StackPanel Margin="-24,0,0,0" 
					Orientation="Horizontal">
                                        <Image VerticalAlignment="Center" 
					Width="16" Height="16" 
					Source="Resources/Images/openHS.png"/>
                                        <TextBlock Margin="8,0,0,0" 
					Text="Project/Solution" 
					VerticalAlignment="Center"/>
                                    </StackPanel>
                                </local:MenuItem.Header>
                            </local:MenuItem>
                            <local:MenuItem Click="OnMenuClick" Header="Web Site"/>
                            <local:MenuItem Click="OnMenuClick" Header="File"/>
                            <local:MenuItem Click="OnMenuClick" Header="Convert"/>
                        </local:MenuItem>
                        <local:MenuItem Header="-"/>
                        <local:MenuItem Header="Add">
                            <local:MenuItem Click="OnMenuClick" Header="New Project"/>
                            <local:MenuItem Click="OnMenuClick" Header="Web Site"/>
                            <local:MenuItem Header="-"/>
                            <local:MenuItem Click="OnMenuClick" 
						Header="Existing Project"/>
                            <local:MenuItem Click="OnMenuClick" 
						Header="Existing Web Site"/>
                        </local:MenuItem>
                        <local:MenuItem Header="-"/>
                        <local:MenuItem KeyGesture="Shift+E" Click="OnMenuClick" 
							Header="Close"/>
                        <local:MenuItem Header="-"/>
                        <local:MenuItem Click="OnFullScreenClick" Header="Full _Screen"/>
                        <local:MenuItem Header="-"/>
                        <local:MenuItem Click="OnMenuClick" Header="Exit"/>
                    </local:MenuItem>
                    <local:MenuItem Header="_Edit">
                        <local:MenuItem Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <Image VerticalAlignment="Center" 
				Width="16" Height="16" 
				Source="Resources/Images/Edit_UndoHS.png"/>
                                    <ContentControl Tag="AccessKey" 
				Margin="8,0,0,0" Content="_Undo" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <Image VerticalAlignment="Center" 
				Width="16" Height="16" 
				Source="Resources/Images/Edit_RedoHS.png"/>
                                    <ContentControl Tag="AccessKey" 
				Margin="8,0,0,0" Content="_Redo" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem Header="-"/>
                        <local:MenuItem KeyGesture="Shift+X" Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <Image VerticalAlignment="Center" 
				Width="16" Height="16" 
				Source="Resources/Images/CutHS.png"/>
                                    <TextBlock Margin="8,0,0,0" Text="Cut" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem KeyGesture="Shift+C" Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <Image VerticalAlignment="Center" 
				Width="16" Height="16" 
				Source="Resources/Images/CopyHS.png"/>
                                    <TextBlock Margin="8,0,0,0" 
				Text="Copy" VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem KeyGesture="Shift+V" Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <Image VerticalAlignment="Center" 
				Width="16" Height="16" 
				Source="Resources/Images/PasteHS.png"/>
                                    <TextBlock  Margin="8,0,0,0" 
				Text="Paste" VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                    </local:MenuItem>
                    <local:MenuItem Header="_Help">
                        <local:MenuItem Click="OnMenuClick" Header="About"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Deep Items _1">
                        <local:MenuItem Header="Item 1.1">
                            <local:MenuItem Header="Item 1.1.1">
                                <local:MenuItem Header="Item 1.1.1.1" 
				Click="OnMenuClick"/>
                                <local:MenuItem Header="Item 1.1.1.2" 
				Click="OnMenuClick"/>
                                <local:MenuItem Header="Item 1.1.1.3" 
				Click="OnMenuClick"/>
                            </local:MenuItem>
                            <local:MenuItem Header="Item 1.1.2">
                                <local:MenuItem Header="Item 1.1.2.1" 
				Click="OnMenuClick"/>
                                <local:MenuItem Header="Item 1.1.2.2" 
				Click="OnMenuClick"/>
                                <local:MenuItem Header="Item 1.1.2.3" 
				Click="OnMenuClick"/>
                            </local:MenuItem>
                        </local:MenuItem>
                        <local:MenuItem Header="Item 1.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 1.3" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 1.4" Click="OnMenuClick"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Check Boxes">
                        <local:MenuItem x:Name="view_checked_1" IsChecked="True" 
				Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <CheckBox IsChecked=
				"{Binding ElementName=view_checked_1, 
				Path=IsChecked, Mode=TwoWay}" Width="16" 
				Height="16" VerticalAlignment="Center"/>
                                    <ContentControl Tag="AccessKey" 
				Margin="8,0,0,0" Content="Status _bar" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem Header="-"/>
                        <local:MenuItem x:Name="view_checked_gr_1" 
				GroupName="RadioButtonGroup_1" 
				IsChecked="True" Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <RadioButton IsChecked=
				"{Binding ElementName=view_checked_gr_1, 
				Path=IsChecked, Mode=TwoWay}" Width="16" 
				Height="16" VerticalAlignment="Center"/>
                                    <ContentControl Tag="AccessKey" 
				Margin="8,0,0,0" Content="Full Mode" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem x:Name="view_checked_gr_2" 
				GroupName="RadioButtonGroup_1" 
				IsChecked="False" Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <RadioButton IsChecked=
				"{Binding ElementName=view_checked_gr_2, 
				Path=IsChecked, Mode=TwoWay}" Width="16" 
				Height="16" VerticalAlignment="Center"/>
                                    <ContentControl Tag="AccessKey" 
				Margin="8,0,0,0" Content="Compact Mode" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                        <local:MenuItem x:Name="view_checked_gr_3" 
				GroupName="RadioButtonGroup_1" 
				IsChecked="False" Click="OnMenuClick">
                            <local:MenuItem.Header>
                                <StackPanel Margin="-24,0,0,0" Orientation="Horizontal">
                                    <RadioButton IsChecked=
				"{Binding ElementName=view_checked_gr_3, 
				Path=IsChecked, Mode=TwoWay}" Width="16" 
				Height="16" VerticalAlignment="Center"/>
                                    <ContentControl Tag="AccessKey" 
				Margin="8,0,0,0" Content="Simple Mode" 
				VerticalAlignment="Center"/>
                                </StackPanel>
                            </local:MenuItem.Header>
                        </local:MenuItem>
                    </local:MenuItem>
                    <local:MenuItem Header="Item 3" Click="OnMenuClick">
                        <local:MenuItem Header="Item 3.1" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 3.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 3.3" Click="OnMenuClick"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Item 4" Click="OnMenuClick">
                        <local:MenuItem Header="Item 4.1" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 4.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 4.3" Click="OnMenuClick"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Item 5" Click="OnMenuClick">
                        <local:MenuItem Header="Item 5.1" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 5.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 5.3" Click="OnMenuClick"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Item 6" Click="OnMenuClick">
                        <local:MenuItem Header="Item 6.1" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 6.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 6.3" Click="OnMenuClick"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Item 7" Click="OnMenuClick">
                        <local:MenuItem Header="Item 7.1" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 7.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 7.3" Click="OnMenuClick"/>
                    </local:MenuItem>
                    <local:MenuItem Header="Item 8" Click="OnMenuClick">
                        <local:MenuItem Header="Item 8.1" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 8.2" Click="OnMenuClick"/>
                        <local:MenuItem Header="Item 8.3" Click="OnMenuClick"/>
                    </local:MenuItem>
                </local:Menu>
            </Grid>
        </Border>
    <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" 
		Foreground="Red" FontSize="16" x:Name="_click_text" Grid.Row="1" 
		Text="Nothing was clicked!"/>
  </Grid>
</UserControl>

您所要做的就是在 XAML 中放入几行代码即可看到它的效果:

      <Grid  x:Name="LayoutRoot" Background="#FF75CEEE">
        <!--GRID ROW DEFENITIONS-->
        <Grid.RowDefinitions>
          <RowDefinition Height="25"/>
          <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <!--MAIN SECTION-->
        <local:Menu Grid.Row="0" BorderBrush="#FF117396" 
	BorderThickness="2,2,2,2" Foreground="#FF1187B1" Margin="0,0,0,0">
          <MenuItem Header="File">
            <MenuItem Header="Open"/>
          </MenuItem>
        </local:Menu>
      <Grid

好了,我想这就差不多了。如果有人需要代码方面的帮助,请随时给我发电子邮件,或直接回复。

© . All rights reserved.