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

我面临的问题是,它只在第二次打开菜单后才更新上下文菜单.

例如:服务正在运行,因此应该禁用"Start"按钮.一旦我点击Stop,我需要打开上下文菜单两次,它才能更新并启用Start按钮.

有没有比我在这里创建的更好的方法来更新上下文菜单?

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;
    }
}

推荐答案

这里的主要问题是,当菜单本身即将出现时,您将通过单击Tray图标来替换ConextMenu.菜单的实例已经初始化为old one.

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

我将用ConextMenuBar替换ConextMenu,因为前者在.NET中已经是deprecated了.

I've also added a Close Menu Item, which is used to terminate the Process.
This is also going to dispose both the ContextMenuStrip and the NotifyIcon, so it's removed from the Tray

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 startMenuState = false;
        bool stopMenuState = false;
        sc.Refresh();
        if (sc.Status.HasFlag(ServiceControllerStatus.Running) || sc.Status.HasFlag(ServiceControllerStatus.StartPending)) {
            stopMenuState = true;
        }
        else {
            startMenuState = true;
        }
        cms.Items["Start"].Enabled = startMenuState;
        cms.Items["Stop"].Enabled = stopMenuState;
    }

    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();
    }
}

In case someone wants to test this and doesn't know how, in 100 replace the default:

Application.Run(new Form1());

有:

Application.Run(new TrayAppContext());

Csharp相关问答推荐

如何使用Microsoft Curve API从搜索的文件中获取内容(文本)?

无法使用ternal- .net修复可空警告

我可以 suppress 规则CS 9035一次吗?

在命令行中使用时安装,但在单击时不会安装

需要深入了解NpgSQL DateTimeOffset处理

CS0103 dlibdotnet和www.example.com facerect不在上下文中

Blazor Foreach仅渲染最后一种 colored颜色

从Blob存储中提取tar.gz文件并将提取结果上载到另一个Blob存储

在C#中,有没有一种方法可以集中定义跨多个方法使用的XML参数描述符?

获取具有AutoFaces的所有IOptions对象的集合

如何使用C#获取FireStore中的列表输出文档

在具有不同属性名称的两个类之间创建关系

使用Entity Framework6在对象中填充列表会导致列表大多为空

从另一个不同 struct 的数组创建Newtonsoft.Json.Linq.J数组

ASP.NET Core MVC将值从视图传递到控制器时出现问题

链接到字典字符串.拆分为(.Key,.Value)

此异步方法在重写方法中缺少等待运算符警告

如何使用实体框架核心对字符串_agg使用强制转换为varchar(Max)

如何解决System.StackOverflowException:抛出System.StackOverflowException类型的异常.&# 39;生成随机代码时发生异常?

与Visual Studio 2022中的.NET框架相比,如何在.NET Core 6中获取错误输出的窗口句柄