我可以在c# (WPF)中单击而不是鼠标上打开子菜单项吗?
本文关键字:鼠标 菜单项 WPF 单击 我可以 | 更新日期: 2023-09-27 18:13:59
我有一个由3个级别的菜单项组成的菜单,第一个级别只在单击时打开其子菜单,第二个子菜单级别在鼠标结束时打开其子菜单,我会改变这种行为,以获得一个只在单击
submenu
的Opening / Closing
由MenuItem.IsSubmenuOpen
的性质决定。我们所需要的就是相应地改变
MenuItem.IsSubmenuOpen
的值,这样它就不会被覆盖。我们知道
Coercion
具有最高优先级。对于
Coerce
的值,我们需要提供一个CoerceValueCallback
,为此我们需要创建一个新的MenuItem
控件,和一个新的Menu
来使用我们的新MenuItem
。
MenuOpenOnlyOnClick.xaml
<Menu
x:Class=" WpfStackOverflow.Controls.MenuOpenOnlyOnClick"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</Menu>
MenuOpenOnlyOnClick.xaml.cs
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Linq;
namespace WpfStackOverflow.Controls
{
/// <summary>
/// Interaction logic for MenuOpenOnlyOnClick.xaml
/// </summary>
public partial class MenuOpenOnlyOnClick : Menu
{
public MenuOpenOnlyOnClick()
{
InitializeComponent();
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MenuItemNew();
}
}
public class MenuItemNew : MenuItem
{
#region Constructors
static MenuItemNew()
{
MenuItem.IsSubmenuOpenProperty.OverrideMetadata(typeof(MenuItemNew),
new FrameworkPropertyMetadata(false, MenuItem.IsSubmenuOpenProperty.DefaultMetadata.PropertyChangedCallback, CoerceIsSubmenuOpen));
}
public MenuItemNew()
{
this.PreviewMouseLeftButtonDown += MenuItemNew_PreviewMouseLeftButtonDown;
this.LostFocus += MenuItemNew_LostFocus;
}
#endregion
#region Event Handlers
void MenuItemNew_LostFocus(object sender, RoutedEventArgs e)
{
MenuItemNew item = sender as MenuItemNew;
item.IsMenuItemExpanded = false;
}
void MenuItemNew_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MenuItemNew item = sender as MenuItemNew;
if (item.Role == MenuItemRole.SubmenuHeader)
{
IsMenuItemExpanded = !IsMenuItemExpanded;
IsSubmenuOpen = IsMenuItemExpanded;
}
}
#endregion
#region IsMenuItemExpanded Dependency Property
public bool IsMenuItemExpanded
{
get { return (bool)GetValue(IsExpanderClickedProperty); }
set { SetValue(IsExpanderClickedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsMenuItemExpanded. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsExpanderClickedProperty =
DependencyProperty.Register("IsMenuItemExpanded", typeof(bool), typeof(MenuItemNew),
new PropertyMetadata(false, null));
#endregion
#region Overrides
protected override DependencyObject GetContainerForItemOverride()
{
return new MenuItemNew();
}
#endregion
#region Dependency Property Callbacks
private static object CoerceIsSubmenuOpen(DependencyObject d, object value)
{
if ((bool)value)
{
MenuItemNew item = (MenuItemNew)d;
if (item.Role == MenuItemRole.SubmenuHeader)
{
if (item.IsMenuItemExpanded)
return true;
else
return false;
}
if (!item.IsLoaded)
{
item.Loaded += (s, e) =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Input, new DispatcherOperationCallback(delegate(object param)
{
item.CoerceValue(MenuItemNew.IsSubmenuOpenProperty);
return null;
}), null);
};
return false;
}
}
return value;
}
#endregion
}
}
使用<Window x:Class="WpfStackOverflow.Window17"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:WpfStackOverflow.Controls"
Title="Window17" Height="300" Width="300">
<Grid>
<ctrl:MenuOpenOnlyOnClick VerticalAlignment="Top" >
<ctrl:MenuItemNew Header="File">
<ctrl:MenuItemNew Header="New">
<ctrl:MenuItemNew Header="Project"/>
<ctrl:MenuItemNew Header="Website"/>
</ctrl:MenuItemNew>
<ctrl:MenuItemNew Header="Open"/>
</ctrl:MenuItemNew>
</ctrl:MenuOpenOnlyOnClick>
</Grid>
</Window>
我不确定是否存在更好的解决方案。您可以尝试使用以下代码来抑制在鼠标悬停时打开子菜单,并在单击
时打开它://Suppose menu is the name of your Menu (or ContextMenu)
bool suppressOpen = true;
menu.AddHandler(MenuItem.SubmenuOpenedEvent, new RoutedEventHandler((s, e) =>
{
if (suppressOpen) ((MenuItem)e.Source).IsSubmenuOpen = false;
else suppressOpen = true;
}));
menu.AddHandler(MenuItem.PreviewMouseDownEvent,
new MouseButtonEventHandler((s, e) =>
{
suppressOpen = false;
((MenuItem)e.Source).IsSubmenuOpen = true;
}));