1

我正在尝试创建一个 ContextMenu,其中菜单中的某些项目包含多个项目。您可以将其视为尝试组合 ToolStrip 和 ContextMenuItem。我试过使用 ToolStripControlHost,但这会产生焦点问题。它基本上要求您单击 ToolStrip 中的所有内容两次..

ContextMenuStrip m = new ContextMenuStrip();
ToolStrip tStrip = new ToolStrip(new ToolStripDropDownButton(), new ToolStripButton());
ToolStripControlHost tsHost = new ToolStripControlHost(tStrip);
m.Items.Add(tsHost);

关于如何实现这一目标的任何想法?

4

1 回答 1

1

ContextMenuStrip 对于不像上下文菜单的自定义弹出窗口来说太有吸引力了。这是可取的,因为当用户在菜单外单击时它会自动弹出。虽然它有局限性,但它不是一个很好的控制主机。点击问题是经典问题,CMS 捕获鼠标以检测用户何时在窗口外点击。

这真的应该是一种形式。不过,要使其具有与 CMS 相同的行为,需要做一些工作。您必须检测到窗口外的鼠标点击,以便使窗口消失。像 CMS 那样捕获鼠标是行不通的。一个技巧是使用 IMessageFilter,它可以让您在输入消息被传递到具有焦点的窗口之前先查看它们。这是实现此功能的示例表单:

public partial class MyContextMenu : Form, IMessageFilter {
    public MyContextMenu() {
        InitializeComponent();
        Application.AddMessageFilter(this);
    }
    protected override void OnFormClosed(FormClosedEventArgs e) {
        Application.RemoveMessageFilter(this);
        base.OnFormClosed(e);
    }
    public void Show(Control ctl, Point pos) {
        this.StartPosition = FormStartPosition.Manual;
        this.Location = ctl.PointToScreen(pos);
        while (!(ctl is Form)) ctl = ctl.Parent;
        this.Show((Form)ctl);
    }
    public bool PreFilterMessage(ref Message m) {
        // Detect mouse clicks outside of the form
        if (m.Msg == 0x201 || m.Msg == 0x204 || m.Msg == 0x207 || 
            m.Msg == 0xA1  || m.Msg == 0xA4  || m.Msg == 0xA7) {
            Point pos = new Point(m.LParam.ToInt32());
            Control ctl = Control.FromHandle(m.HWnd);
            if (ctl != null) pos = ctl.PointToScreen(pos);
            pos = this.PointToClient(pos);
            if (pos.X < 0 || pos.Y < 0 || pos.X >= this.Width || pos.Y >= this.Height) {
                this.Close();
            }
        }
        return false;
    }
}

像往常一样使用设计器来设计表单。你至少想给它一个不同的 FormBorderStyle。使用提供的 Show() 方法重载,就像在 CMS 中使用它一样。请注意,仅当您单击应用程序拥有的窗口时才会弹出表单,这与 CMS 不同。功能,而不是错误。

于 2011-03-06T01:09:59.337 回答