ContextMenu disappears/closes 打开后立即

ContextMenu disappears/closes immediately after being opened

我想弄清楚为什么 Windows Forms 应用程序中的 System.Windows.Forms.ContextMenu 在我通过右键单击任务栏上的托盘图标打开它后立即关闭。

这不会立即发生,但奇怪的是在第二次打开时开始发生。 (即我可以 open/close 上下文菜单正常一次,然后问题突然出现 )右键单击后,我可以直观地看到菜单出现了一小部分秒后自动关闭。

这里是 Program.cs 中的 C# 代码。我最初关注的是 Barnacules Nerdgasm 的“#Codegasm”教程之一 (YouTube, GitHub),其中涉及删除项目的表单方面,将其作为唯一的源文件。

using System;
using System.Windows.Forms;
using System.Threading;
using System.Net.NetworkInformation;
using System.Net;
using System.Drawing;

namespace PingGoogleDNS
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            PingGoogleDNS pinger = new PingGoogleDNS();
            pinger.StartPinger();
        }

        class PingGoogleDNS
        {
            #region Global Thread and Icon Objects
            NotifyIcon pingIcon;
            Icon goodIcon = new Icon("good_connection.ico");
            Icon weakIcon = new Icon("weak_connection.ico");
            Icon noIcon = new Icon("no_connection.ico");

            Thread pingWorker;
            MenuItem roundTripTime;
            #endregion

            #region StartPinger
            public void StartPinger()
            {
                pingIcon = new NotifyIcon();
                pingIcon.Icon = noIcon /*disconnectedIcon*/;
                pingIcon.Visible = true;

                MenuItem quit = new MenuItem("Quit / Exit");
                MenuItem name = new MenuItem("Pings Google DNS (8.8.8.8)");
                roundTripTime = new MenuItem("Latest ping: N/A ms");
                ContextMenu contextMenu = new ContextMenu();
                contextMenu.MenuItems.Add(quit);
                contextMenu.MenuItems.Add(name);
                contextMenu.MenuItems.Add(roundTripTime);
                pingIcon.ContextMenu = contextMenu;

                quit.Click += Quit_Click;

                pingWorker = new Thread(new ThreadStart(PingSenderThread));
                pingWorker.Start();
                Console.WriteLine("Start");
            }
            #endregion

            #region Quit Button Handler
            private void Quit_Click(object sender, EventArgs e)
            {
                pingIcon.Dispose();
                pingWorker.Abort();
                Environment.Exit(1);
            }
            #endregion

            #region Ping Sender Thread
            public void PingSenderThread()
            {
                Ping pingSender = new Ping();

                IPAddress iPAddress;
                IPAddress.TryParse("8.8.8.8", out iPAddress);

                PingReply reply;
                try
                {
                    while (true)
                    {
                        try
                        {
                            reply = pingSender.Send(iPAddress, 2000 /* ms */);

                            if (reply.Status == IPStatus.Success)
                            {
                                if (reply.RoundtripTime < 500 /* ms */)
                                    pingIcon.Icon = goodIcon /*connectedIcon*/;
                                else
                                    pingIcon.Icon = weakIcon;

                                pingIcon.Text = "8.8.8.8 ping success! (" + reply.RoundtripTime + " ms)";
                                roundTripTime.Text = "Latest ping: " + reply.RoundtripTime + " ms";

                                Thread.Sleep(1000);
                            }
                        }
                        catch (PingException pe)
                        {
                            pingIcon.Icon = noIcon /*disconnectedIcon*/;
                            pingIcon.Text = "8.8.8.8 ping failure.";
                            roundTripTime.Text = "Latest ping: N/A ms";
                        }
                    }
                }
                catch (ThreadAbortException)
                {
                    // No need to do anything, just catch the ThreadAbortException.
                }
            }
            #endregion
        }
    }
}

我试图通过向 ContextMenuPopupCollapse 事件添加侦听器来调试它,以查看它是否注册为已关闭:

// In StartPinger()
contextMenu.Popup += ContextMenu_Popup;
contextMenu.Collapse += ContextMenu_Collapse;

// ...

private void ContextMenu_Popup(object sender, EventArgs e)
{
     Console.WriteLine("menu opened");
}

private void ContextMenu_Collapse(object sender, EventArgs e)
{
     Console.WriteLine("menu closed");
}

但是,当我右键单击时,我只看到 "menu opened" 被打印到控制台,而没有打印 "menu closed"。这让我感到困惑,我不知道下一步该尝试什么。以前有其他人遇到过这个吗?

您缺少消息循环(a.k.a。消息泵)。将此添加到 Main 函数的末尾。

Application.Run();

每个事件驱动的应用程序(例如您的应用程序)都会在处理事件并分派给处理程序的某个时刻进入无限循环。这就是 Application.Run() 的意思。 (阅读文档的备注部分以获得更完整的描述。)您需要此消息循环才能使任何 UI 相关的东西正常工作,例如鼠标单击、windows 和弹出菜单。

此外,您似乎在尝试从 pinger 线程更新图标时遇到跨线程问题。所有 UI 相关的事情都应该发生在那个主线程上。如果您需要另一个线程来执行网络 IO,例如 ping,那么您将必须 Invoke 任何更新 UI 的代码,以便该代码在主线程的上下文中运行。