【问题标题】:Is there a way to show a "blocking" WinForms ContextMenu?有没有办法显示“阻止”WinForms ContextMenu?
【发布时间】:2011-12-06 16:03:47
【问题描述】:

有没有办法显示ContextMenu 并阻止进一步执行,直到选择了一个项目?特别是,我希望获得类似于 ShowDialog() 的行为,但对于 ContextMenu

直截了当的方法行不通:

ContextMenu cm = new ContextMenu();
cm.MenuItems.Add("1", (s,e) => {value = 1;});
cm.Show(control, location);

因为Click 回调不是直接从Show() 调用,而是在稍后消息循环处理点击事件时调用。

如果你不走运,menu 在事件处理之前被垃圾收集,在这种情况下,事件只是默默地丢失。 (这意味着您不能以这种方式真正为ContextMenus 使用局部变量。)

这似乎有效,但感觉“不干净”:

using (ContextMenu cm = new ContextMenu()) {
    cm.MenuItems.Add("1", (s,e) => {value = 1;});
    cm.Show(control, location);
    Application.DoEvents();
}

有没有更好的办法?

【问题讨论】:

  • 没有。将 Show() 调用之后的任何代码移动到 Collapse 事件的事件处理程序中。或者使用表单,以便您可以使用 ShowDialog。

标签: c# .net winforms garbage-collection contextmenu


【解决方案1】:

对不起第一个答案。这是我尝试过的。我制作了另一个表单,在其中放置了上下文菜单和计时器。Form2 从 Form1 显示为模态,然后计时器在 Form2 上显示上下文菜单。

请注意,Form 2 设置了一些属性:在任务栏中不可见,没有边框,并且大小应与上下文菜单的大小相等。

希望这会有所帮助。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
        {
            Form2 ctxForm = new Form2();
            ctxForm.Location = this.PointToScreen(e.Location);
            ctxForm.Size = new Size(0, 0);
            ctxForm.ShowDialog();
        }
    }



}


public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private void exitToolStripMenuItem_Click(object sender, EventArgs e)
    {
        this.Close();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        //show menu once
        contextMenuStrip1.Show(this, PointToClient(Location));
        contextMenuStrip1.Focus();
        timer1.Enabled = false;
    }

    private void contextMenuStrip1_Closed(object sender, ToolStripDropDownClosedEventArgs e)
    {
        this.Close();
    }
}

【讨论】:

  • 这很好,但不幸的是焦点无法正常工作:如果您在“模态”上下文菜单之外单击另一个表单,则单击不会正确注册。
【解决方案2】:

您可以在 ContextMenu 仍在显示时轻松阻止垃圾收集。

问题在于您使用 lambda 作为菜单项的事件处理程序。这是个 匿名方法,因此本身不附加到任何会导致 ContextMenu 被引用并因此保持活动状态的对象实例。将方法添加到封闭对象,然后创建标准 EventHandler。这样,封闭实例的存在将使 ContextMenu 保持活动状态。不像 C# 1.0 那样简洁和非常,但它会解决问题。

【讨论】:

    【解决方案3】:

    等待菜单不可见。

    ContextMenu cm = new ContextMenu();
    cm.MenuItems.Add("1", (s,e) => {value = 1;});
    cm.Show(control, location);
    while (cm.Visible == true) Application.DoEvents();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 2019-10-20
      • 1970-01-01
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多