Adding MenuItems to Contextmenu for a TrayIcon in a Console app

我的梦境 提交于 2021-01-28 01:11:14

问题


I made a little console app which locks the Mouse to the first screen.
Now I want to create a TrayIcon with a ContextMenu to close the application.

In debug-mode, I can see that the ContextMenu has two Items, just like it should, but it doesn't display the ContextMenu.

An EventHandler shouldn't be needed from what I've read so far.

My code:

static void GenerateTrayIcon()
{
    ContextMenu trayiconmenu = new ContextMenu();
    trayiconmenu.MenuItems.Add(0, new MenuItem("Show", new EventHandler(Show_Click)));
    trayiconmenu.MenuItems.Add(1, new MenuItem("Exit", new EventHandler(Exit_Click)));

    NotifyIcon TrayIcon = new NotifyIcon();
    TrayIcon.Icon = new Icon("Path to .ico");
    TrayIcon.Text = "Cursor is locked to primary screen";
    TrayIcon.Visible = true;
    TrayIcon.ContextMenu = trayiconmenu;
}

static void Exit_Click(object sender, EventArgs e)
{
    Environment.Exit(0);
}

static void Show_Click(object sender, EventArgs e)
{
    // Do something
}

回答1:


To make the NotifyIcon work, you have to start a Message Loop, usually calling Application.Run(). The calling method is also usually marked as single-threaded ([STAThread]).

That's more or less just it.
► Of course you need to dispose of the objects you created. In this case, the NotifyIcon object and the ContextMenu. You can also call Dispose() on the Icon object, in case it's just set to null in the internal NativeWindow.

In the example here, the ConsoleNotifyIcon class object is used to run the Message Loop and receive the ContextMenu items mouse events.
In this case, the Exit click handler just signals the main Thread that an exit request has been queued. It also removes the NotifyIcon from the Notification Area.

The Main Thread can then acknowledge the request and terminate.
It also makes sure, before exiting, that the NotifyIcon has been disposed.

► You can decide to actually use Environment.Exit() in the Exit event, but it's a quite brutal exit procedure. Evaluate whether it won't disrupt the Main Thread operations.

[STAThread]
static void Main(string[] args)
{
    // Start the NotifyIcon Thread
    ConsoleNotifyIcon.GenerateTrayIcon();

    // Do stuff. Check whether an Exit request has been queued
    while (!ConsoleNotifyIcon.IsAppCloseRequest) {
        string input = Console.ReadLine();
        
        // Other operations...
    }

    if (!ConsoleNotifyIcon.IsAppCloseRequest) {
        ConsoleNotifyIcon.Dispose();
    }
}

using System.Threading.Tasks;
using System.Windows.Forms;

public class ConsoleNotifyIcon
{
    // Store these objects as private Fields
    private static NotifyIcon trayIcon;
    private static ContextMenu trayContextMenu;

    // The main public method starts a new Task. It uses a ThreadPool Thread.
    [STAThread]
    public static void GenerateTrayIcon() => Task.Run(() => StartTrayIcon());

    private static void StartTrayIcon() {
        trayContextMenu = new ContextMenu();
        trayContextMenu.MenuItems.Add(0, new MenuItem("Show", Show_Click));
        trayContextMenu.MenuItems.Add(1, new MenuItem("Exit", Exit_Click));

        trayIcon = new NotifyIcon() {
            ContextMenu = trayContextMenu
            Icon = [Some Icon],  // Possibly, use an Icon Resource
            Text = "Cursor is locked to primary screen",
            Visible = true,
        };

        // Setup completed. Starts the Message Loop
        Application.Run();
    }

    public static bool IsAppCloseRequest = false;

    static void Exit_Click(object sender, EventArgs e) {
        // Dispose of the objects created. 
        // Also removes the NotifyIcon from the Tray Notification Area
        Dispose();
        // Signals the App Exit request. You could also use an Event.
        IsAppCloseRequest = true;
    }

    static void Show_Click(object sender, EventArgs e) {
        // Do something
    }

    public static void Dispose() {
        trayIcon.Icon?.Dispose();
        trayIcon.Dispose();
        trayContextMenu.Dispose();
    }
}



回答2:


With Jimi's answer i got it sorted. I ran the method which creates the TrayIcon and the ContextMenu in another thread, just like this:

    static void Main(string[] args)
    {
        Thread thread = new Thread(GenerateTrayIcon);
        thread.Start();
                    

        while (1 > 0)
        {
            int currentX = Cursor.Position.X;
            int currentY = Cursor.Position.Y;

            int screenX = Screen.PrimaryScreen.Bounds.Width;
            int screenY = Screen.PrimaryScreen.Bounds.Height;

            if (currentX > screenX)
            {
                Cursor.Position = new Point(screenX, currentY);
            }

            if (currentY > screenY)
            {
                Cursor.Position = new Point(screenY, currentX);
            }
        }
    }
    
    
    static void GenerateTrayIcon()        
    {                       
        trayiconmenu.MenuItems.Add(0, new MenuItem("Exit", new EventHandler(Exit_Click)));
        
        TrayIcon.Icon = new Icon("C:\\Users\\lukas\\Desktop\\auge.ico");
        TrayIcon.Text = "Cursor is locked to primary screen";
        TrayIcon.Visible = true;
        TrayIcon.ContextMenu = trayiconmenu;

        Application.Run();
    }

    static void Exit_Click(object sender, EventArgs e)
    {
        Environment.Exit(0);
        TrayIcon.Dispose();
    }        
}

}



来源:https://stackoverflow.com/questions/65048083/adding-menuitems-to-contextmenu-for-a-trayicon-in-a-console-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!