NotifyIcon 的上下文菜单不会立即更新

问题描述 投票:0回答:1

我正在创建一个托盘图标应用程序,其中包含一个包含 3 个项目的上下文菜单。托盘图标用于控制某个服务的运行,以便用户可以快速启动或停止它。根据服务状态,如果服务已在运行,我想禁用

Start
按钮。

我面临的问题是它正在更新 ContextMenu,但仅在第二次打开菜单后才更新。

例如:服务正在运行,因此应禁用“启动”按钮。单击

Stop
后,我需要打开 ContextMenu 两次才能更新并启用
Start
按钮。

有没有比我在这里创建的更好的方法来更新 ContextMenu?

class TrayApp : ApplicationContext
{
    private NotifyIcon trayIcon;
    private ServiceController sc;
    
    public TrayApp()
    {
        sc = new ServiceController("RamLogger");

        trayIcon = new NotifyIcon()
        {
            Icon = Properties.Resources.icon1,
            Text = "RamLogger",
            ContextMenu = GetContextMenu(),
            Visible = true
        };
        trayIcon.MouseClick += new MouseEventHandler(OnClick);
    }

    void OnClick(object sender, MouseEventArgs e)
    {
        if(e.Button == MouseButtons.Right)
        {
            trayIcon.ContextMenu = GetContextMenu();
        }
    }
    
    private ContextMenu GetContextMenu()
    {
        sc.Refresh();
        ContextMenu cm = new ContextMenu();
        cm.MenuItems.Clear();

        if (sc.Status == ServiceControllerStatus.Running || sc.Status == ServiceControllerStatus.StartPending)
        {
            cm.MenuItems.Add(new MenuItem("Status: Running"));
            cm.MenuItems.Add(new MenuItem("-"));
            cm.MenuItems.Add(new MenuItem("Start", Start) { Enabled = false });
            cm.MenuItems.Add(new MenuItem("Stop", Stop) { Enabled = true });
        }
        else
        {
            cm.MenuItems.Add(new MenuItem("Status: Stopped"));
            cm.MenuItems.Add(new MenuItem("-"));
            cm.MenuItems.Add(new MenuItem("Start", Start) { Enabled = true });
            cm.MenuItems.Add(new MenuItem("Stop", Stop) { Enabled = false });
        }
        return cm;
    }
}
c# .net winforms notifyicon servicecontroller
1个回答
1
投票

这里的主要问题是,当菜单本身即将显示时,您要替换上下文菜单,单击托盘图标。菜单的实例已经初始化为旧的

您只需创建一次菜单,然后根据您正在查询的服务的状态启用/禁用项目。

我将用 ContextMenuStrip 替换 ContextMenu,因为前者在 .NET 中已被“弃用”。它还将简化从 .NET Framework 到 .NET 6+ 的过渡 我还添加了一个

Close

菜单项,用于终止进程。

这也将处理 ContextMenuStrip 和 NotifyIcon,因此它会从托盘中删除

using System.ServiceProcess; using System.Windows.Forms; class TrayAppContext : ApplicationContext { private readonly NotifyIcon trayIcon; private readonly ServiceController sc; private readonly ContextMenuStrip cms = null; public TrayAppContext() { sc = new ServiceController("RamLogger"); cms = GetContextMenu(); trayIcon = new NotifyIcon() { Icon = Properties.Resources.icon1, Text = "RamLogger", ContextMenuStrip = cms, Visible = true }; trayIcon.MouseClick += OnClick; } void OnClick(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Right) return; bool serviceAvailable = sc.Status.HasFlag(ServiceControllerStatus.Running) || sc.Status.HasFlag(ServiceControllerStatus.StartPending); cms!.Items["Start"].Enabled = !serviceAvailable; cms!.Items["Stop"].Enabled = serviceAvailable; } private ContextMenuStrip GetContextMenu() { var cms = new ContextMenuStrip(); cms.Items.Add("Status", null, null); cms.Items.Add("-"); cms.Items.Add(new ToolStripMenuItem("Start", null, Start, "Start") { Enabled = false }); cms.Items.Add(new ToolStripMenuItem("Stop", null, Stop, "Stop") { Enabled = false }); cms.Items.Add("-"); cms.Items.Add(new ToolStripMenuItem("Close", null, Close, "Close") { Enabled = true }); return cms; } void Start(object sender, EventArgs e) => sc.Start(); void Stop(object sender, EventArgs e) => sc.Stop(); void Close(object sender, EventArgs e) { cms?.Dispose(); trayIcon.Dispose(); ExitThreadCore(); } }

如果有人想测试这个但不知道如何做,请在

Program.cs中替换默认值

Application.Run(new Form1());

与:

Application.Run(new TrayAppContext());

© www.soinside.com 2019 - 2024. All rights reserved.