Silverlight 3.0 下拉菜单






4.35/5 (18投票s)
这是 SL 3.0 下拉菜单的实现,类似于应用程序主菜单。
示例图片
打开的菜单项
---
支持复选框和单选按钮(分组)
快速菜单项访问(下划线字符)
在线演示说明
菜单激活快捷键设置为“Ctrl”键。按下菜单激活键时,具有快速访问的菜单项会显示下划线字符。如果您使用“Ctrl”键激活菜单,只需在释放“Ctrl”键的同时按下带下划线的字符即可打开菜单项。
引言
首先,对于您在阅读本文时可能遇到的语言问题,我深表歉意。英语是我的第二语言,因此对于任何拼写错误或语言错误,我都很抱歉。
如果您查看文章中的图片,我认为无需解释这是什么。我驱动的主要想法是开发可能很有用的基本应用程序功能。我认为每个人都明白 Silverlight 使我们能够实现类似 Windows 的应用程序,当涉及到应用程序命令导航时,我的菜单可能会非常方便。
背景
这个控件的实现是我正在开发的一个业务应用程序的一部分。整个应用程序包含许多附加类和控件,我将在稍后发布。最终的包将包括:
WindowBase
类(模态模式支持) - 正在开发中...- 工具栏 - 正在开发中...
- 桌面(类似 Windows 的桌面模拟) - 正在开发中...
- 任务栏 - 正在开发中
- DesktopApplication(这是用于桌面插件开发的基类)- 正在开发中...
开发工具
编译和运行此示例所需的开发工具列在下面:
- Microsoft Visual Studio 2008
- Microsoft Expression Blend 3.0 (RCW)
- Silverlight SDK 3.0 (RCW)
- Microsoft Visual Studio Tools for Silverlight 3.0
- Microsoft Silverlight Toolkit 2009 年 7 月版
- Silverlight 3.0 RCW 开发者运行时
主要特点
- 鼠标导航
- 键盘导航
- 快捷键支持
- 快速菜单项访问(类似于快捷键)
- 混合导航模式(鼠标+键盘)
- 复选框支持
- 单选按钮支持(菜单项分组)
注意(快捷键支持限制):
让我解释一下使用快捷键功能。当您的应用程序注册快捷键时,它可能不起作用。发生这种情况是因为浏览器本身会处理特定的快捷键。一个小例子。我在我的示例应用程序中注册了一个快捷键作为动作(关闭 - Ctrl+E)。我从未收到事件到 SL 输入子系统,因为浏览器处理了此事件,导致浏览器窗口顶部的搜索栏被激活。
关于使用快捷键,您需要知道的第二件事是,输入子系统在全屏模式下限制键盘导航。
代码背景
代码包含以下类:
Menu
- 根菜单实现MenuItem
- 菜单项实现MenuTracker
- 此对象跟踪鼠标/键盘事件,负责菜单项/子菜单项弹出功能/键盘导航MenuKeyGesture
- 菜单项快捷键容器MenuKeyGestureTypeConverter
- 菜单项 XAML 快捷键字符串转换器ItemAccessKey
- 菜单项快速访问键容器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
类构造函数的第三个参数是根元素,它将捕获并处理鼠标事件,例如 MouseEnter
、MouseMove
和 MouseDown
。这就是根菜单项被填充和高亮显示的方式。控制子菜单项弹出行为也使用相同的方式。每个 MenuItem
类实例都包含一个 MenuTracker
实例,而前面提到的参数是特定 MenuItem
的 Popup
对象。
鼠标事件处理程序在“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
好了,我想这就差不多了。如果有人需要代码方面的帮助,请随时给我发电子邮件,或直接回复。