我可以在c# (WPF)中单击而不是鼠标上打开子菜单项吗?

本文关键字:鼠标 菜单项 WPF 单击 我可以 | 更新日期: 2023-09-27 18:13:59

我有一个由3个级别的菜单项组成的菜单,第一个级别只在单击时打开其子菜单,第二个子菜单级别在鼠标结束时打开其子菜单,我会改变这种行为,以获得一个只在单击

我可以在c# (WPF)中单击而不是鼠标上打开子菜单项吗?

时打开其子菜单的子菜单
  • submenuOpening / ClosingMenuItem.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;
}));