如何在右键单击菜单项时显示上下文菜单

10

我正在将一个MFC应用程序移植到.NET WinForms。在MFC应用程序中,您可以右键单击菜单或上下文菜单项,然后我们会显示另一个带有诊断和配置项目的上下文菜单。我正在尝试将此功能移植到.NET,但我遇到了麻烦。

我已经能够捕获右键单击,禁用基础菜单的单击并在正确位置弹出上下文菜单,但原始菜单在失去焦点后立即消失。

在MFC中,我们通过使用TPM_RECURSE标志调用TrackPopupMenuEx来显示新的上下文菜单。

.NET中的ContextMenu和较新的ContextMenuStrip类仅具有Show方法。有人知道如何在.NET中实现这一点吗?

编辑

我已经尝试通过p/invoke使用TrackPopupMenuEx,但这将限制您使用ContextMenu而不是ContextMenuStrip,这在我们的应用程序中看起来不协调。它还是不能正常工作。它不适用于新的MenuStrip和ContextMenuStrip。

我还尝试对ToolStripMenuItem进行子类化,以查看是否可以向其中添加上下文菜单。这对于MenuStrip有效,但ContextMenuStrip仍允许右键单击事件通过。


为什么不使用子菜单,这样当你悬停在菜单项上时,它会显示更多的项目? - Kyle Trauberman
我们的许多菜单已经有子菜单了。我们使用这个功能来让管理员调整菜单的安全性。例如,右键单击一个菜单并将其设置为禁用。这将存储在数据库中,并对所有用户生效。 - Rob Prouse
3个回答

11

由于评论,进行修改:

输入:

protected override void OnClick(EventArgs e)
{
    if (SecondaryContextMenu == null || MouseButtons != MouseButtons.Right)
    {
        base.OnClick(e);
    }
}

这部分内容
 MouseButtons != MouseButtons.Right

由于Form类继承了Control类,因此对Control.MouseButtons进行调用时,代码应该能够编译通过。直接调用MouseButtons属性即可。

希望这可以帮助您:

public partial class Form1 : Form
{
    class CustomToolStripMenuItem : ToolStripMenuItem
    {
        private ContextMenuStrip secondaryContextMenu;

        public ContextMenuStrip SecondaryContextMenu
        {
            get
            {
                return secondaryContextMenu;
            }
            set
            {
                secondaryContextMenu = value;
            }
        }

        public CustomToolStripMenuItem(string text)
            : base(text)
        { }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (secondaryContextMenu != null)
                {
                    secondaryContextMenu.Dispose();
                    secondaryContextMenu = null;
                }
            }

            base.Dispose(disposing);
        }

        protected override void OnClick(EventArgs e)
        {
            if (SecondaryContextMenu == null || MouseButtons != MouseButtons.Right)
            {
                base.OnClick(e);
            }
        }
    }

    class CustomContextMenuStrip : ContextMenuStrip
    {
        private bool secondaryContextMenuActive = false;
        private ContextMenuStrip lastShownSecondaryContextMenu = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (lastShownSecondaryContextMenu != null)
                {
                    lastShownSecondaryContextMenu.Close();
                    lastShownSecondaryContextMenu = null;
                }
            }
            base.Dispose(disposing);
        }

        protected override void OnControlAdded(ControlEventArgs e)
        {
            e.Control.MouseClick += new MouseEventHandler(Control_MouseClick);
            base.OnControlAdded(e);
        }

        protected override void OnControlRemoved(ControlEventArgs e)
        {
            e.Control.MouseClick -= new MouseEventHandler(Control_MouseClick);
            base.OnControlRemoved(e);
        }

        private void Control_MouseClick(object sender, MouseEventArgs e)
        {
            ShowSecondaryContextMenu(e);
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            ShowSecondaryContextMenu(e);
            base.OnMouseClick(e);
        }

        private bool ShowSecondaryContextMenu(MouseEventArgs e)
        {
            CustomToolStripMenuItem ctsm = this.GetItemAt(e.Location) as CustomToolStripMenuItem;

            if (ctsm == null || ctsm.SecondaryContextMenu == null || e.Button != MouseButtons.Right)
            {
                return false;
            }

            lastShownSecondaryContextMenu = ctsm.SecondaryContextMenu;
            secondaryContextMenuActive = true;
            ctsm.SecondaryContextMenu.Closed += new ToolStripDropDownClosedEventHandler(SecondaryContextMenu_Closed);
            ctsm.SecondaryContextMenu.Show(Cursor.Position);
            return true;
        }

        void SecondaryContextMenu_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            ((ContextMenuStrip)sender).Closed -= new ToolStripDropDownClosedEventHandler(SecondaryContextMenu_Closed);
            lastShownSecondaryContextMenu = null;
            secondaryContextMenuActive = false;
            Focus();
        }

        protected override void OnClosing(ToolStripDropDownClosingEventArgs e)
        {
            if (secondaryContextMenuActive)
            {
                e.Cancel = true;
            }

            base.OnClosing(e);
        }
    }

    public Form1()
    {
        InitializeComponent();


        CustomToolStripMenuItem itemPrimary1 = new CustomToolStripMenuItem("item primary 1");
        itemPrimary1.SecondaryContextMenu = new ContextMenuStrip();
        itemPrimary1.SecondaryContextMenu.Items.AddRange(new ToolStripMenuItem[] { 
            new ToolStripMenuItem("item primary 1.1"),
            new ToolStripMenuItem("item primary 1.2"),
        });

        CustomToolStripMenuItem itemPrimary2 = new CustomToolStripMenuItem("item primary 2");
        itemPrimary2.DropDownItems.Add("item primary 2, sub 1");
        itemPrimary2.DropDownItems.Add("item primary 2, sub 2");
        itemPrimary2.SecondaryContextMenu = new ContextMenuStrip();
        itemPrimary2.SecondaryContextMenu.Items.AddRange(new ToolStripMenuItem[] { 
            new ToolStripMenuItem("item primary 2.1"),
            new ToolStripMenuItem("item primary 2.2"),
        });

        CustomContextMenuStrip primaryContextMenu = new CustomContextMenuStrip();
        primaryContextMenu.Items.AddRange(new ToolStripItem[]{
            itemPrimary1,
            itemPrimary2
        });

        this.ContextMenuStrip = primaryContextMenu;
    }
}

谢谢,这对于ContextMenuStrip有效,并且已经修改为MenuStrip的顶级菜单。我仍然无法使其适用于MenuStrip的子菜单,但我认为这只是GetItemAt调用的问题。 - Rob Prouse
CustomToolstripMenuItem类中的OnClick覆盖无法编译。不能有(MouseButtons != MouseButtons.Right)这一行代码。这行代码的意图是什么?如何在click事件中确定按下了哪个按钮? - Stuart Helwig
这是 Control 类的一个静态属性。(Control.MouseButtons != MouseButtons.Right)。我没有显式调用 [Control.]MouseButtons,因为 Form 继承了 Control 类。 - Eren Aygunes

2

你可能需要使用p/invoke方法。

[DllImport("user32.dll")]
static extern bool TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y,
IntPtr hwnd, IntPtr lptpm);

const int TPM_RECURSE = 0x0001; 

我已经使用旧的ContextMenu部分地解决了这个问题,但是无法使用ContextMenuStrip。即使如此,它仍然存在焦点和高亮显示方面的一些问题,而且旧的ContextMenu类与更新的菜单不兼容。 - Rob Prouse

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接