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
}
}
}
我试图通过向 ContextMenu
的 Popup
和 Collapse
事件添加侦听器来调试它,以查看它是否注册为已关闭:
// 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 的代码,以便该代码在主线程的上下文中运行。
我想弄清楚为什么 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
}
}
}
我试图通过向 ContextMenu
的 Popup
和 Collapse
事件添加侦听器来调试它,以查看它是否注册为已关闭:
// 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 的代码,以便该代码在主线程的上下文中运行。