ContexMenuStrip奇怪的特定怪异行为

时间:2017-08-21 10:11:21

标签: c# winforms

我正在测试我在应用中遇到的一个奇怪的错误,最后能够创建一个简单的复制:

using System;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var notifyIcon1 = new NotifyIcon();
        notifyIcon1.Icon = new Form().Icon;
        notifyIcon1.Visible = true;
        var contextMenuStrip1 = new ContextMenuStrip();
        ToolStripMenuItem menu1 = new ToolStripMenuItem();
        menu1.Text = "test";
        contextMenuStrip1.Items.Add(menu1);
        contextMenuStrip1.Items.Add("t1");
        contextMenuStrip1.Items.Add("t2");

        notifyIcon1.ContextMenuStrip = contextMenuStrip1;
        var timer = new System.Timers.Timer();
        timer.Interval = 3000;
        timer.Elapsed += (sender, e) =>  /* Runs in a different thread from UI thread.*/
        {
            if (contextMenuStrip1.InvokeRequired)
                contextMenuStrip1.Invoke(new Action(() =>
                {
                    menu1.DropDownItems.Add(e.SignalTime.ToString() + "extra");
                    menu1.DropDownItems.Add(e.SignalTime.ToString());
                }));
            else
            {
                menu1.DropDownItems.Add(e.SignalTime.ToString() + "extra");
                menu1.DropDownItems.Add(e.SignalTime.ToString());
            }
        };
        timer.Start();
        Application.Run();
    }
}

请注意,上下文菜单不会打开,但执行以下任何操作都可以打开:

  • 删除每次执行时添加的“额外”下拉项。 (确切地说 每次执行仅添加0或1)
  • 删除InvokeRequired == false上的部分代码(这允许每次执行添加多个项目)
  • 删除t1t2元素。 (它仍然没有 根目录中的其他项目

这是一个错误还是我做错了什么?

编辑: 额外的条件(感谢@derape):

  • 如果您将else分支移动到单独的方法,它会起作用,但如果您在InvokeRequired分支中使用相同的方法则不行。但是使用具有不同名称和相同代码的2方法可以正常工作。

可能的解决方法是在满月时跳舞时穿着老虎的皮肤。

1 个答案:

答案 0 :(得分:1)

如果您查看InvokeRequired,您会看到IsHandleCreated的明确检查会返回false。返回值并不意味着您不必调用,它只是意味着您无法调用。

让你更加困惑:你必须调用,但你还不能。

如果还没有创建句柄(并且只是遗漏项目),或者组织单独的队列来存储项目直到句柄可用,您可以决定不做任何事情,类似于:

var items = new List<string>();
timer.Elapsed += (sender, e) =>
{
    if (contextMenuStrip1.IsHandleCreated)  // always invoke, but check for handle
        contextMenuStrip1.Invoke(new Action(() =>
        {

            menu1.DropDownItems.Add(e.SignalTime.ToString() + "extra");
            menu1.DropDownItems.Add(e.SignalTime.ToString());
            contextMenuStrip1.Refresh();
        }));
    else
    {
        lock (items)
        {
            items.Add(e.SignalTime.ToString() + "extra");
            items.Add(e.SignalTime.ToString());
        }
    }
};
contextMenuStrip1.HandleCreated += (s, e) =>
{
    lock (items)
    {
        foreach (var item in items)
            menu1.DropDownItems.Add(item);
        contextMenuStrip1.Refresh();
    }
    items = null;
};

另一个注意事项:如果项目已添加到子菜单,则需要调用Refresh,同时菜单已打开,但子菜单尚未显示,这是winforms的奇怪之处。