hecate
hecate

Reputation: 632

NotifyIcon not responsing to changes in its ContextMenu

My (formless system-tray only) program has a NotifyIcon whose ContextMenu value must change dynamically on the running course of the program. The notification icon object is instantiated when the program starts as follows:

notifyIcon = new NotifyIcon()
{
    Icon = new Icon(@"C:\Users\basil\documents\visual studio 2015\Projects\Project Repository\Project Repository\Program Icon.ico"),
    Text = "My Program Name",
    Visible = false // Don't make it visible yet!
};
notifyIcon.ContextMenu = new ContextMenu();
notifyIcon.ContextMenu.MenuItems.AddRange(new MenuItem[] { new MenuItem("Host Service") { Index = 0 }, new MenuItem("Backup Configurations", new EventHandler(delegate { bckConf.Show(); })) { Index = 1 }, new MenuItem("Network Info", new EventHandler(delegate { new form_NetworkInfo().Show(); })) { Index = 2 }, new MenuItem("About", new EventHandler(delegate { new form_AboutUs().Show(); })) { Index = 3 }, new MenuItem("Exit", new EventHandler(delegate { TerminateProgram(ProgramTerminateReason.UserRequest); })) { Index = 4 } }); 

In order to change the ContextMenu of the NotifyIcon, a public method named ChangeNotifyIconState is created as menu items need to be changed as per the results generated by the objects of other classes in the program whose declaration is as follows: (Do disregard the variables of the "CurrentServiceState" data-type and note that the NotifyIcon object is declared as a class variable)

static public void ChangeNotifyIconState(CurrentServiceState state)
{
   notifyIcon.ContextMenu.MenuItems[0].MenuItems.Clear(); // First clear all menu items of the to-be-updated block
   if (state==CurrentServiceState.Running)
   { // Service has started running; disable the start button and activate the stop and restart buttons
      notifyIcon.ContextMenu.MenuItems[0].MenuItems.Add(new MenuItem("Start service", new EventHandler(delegate { HostServices.Start(); })) { Index = 0, Enabled = false });
      notifyIcon.ContextMenu.MenuItems[0].MenuItems.Add(new MenuItem("Stop service", new EventHandler(delegate { HostServices.Stop(); })) { Index = 0, Enabled = true });
      notifyIcon.ContextMenu.MenuItems[0].MenuItems.Add(new MenuItem("Restart service", new EventHandler(delegate { HostServices.Restart(); })) { Index = 0, Enabled = true });
   } else if (state==CurrentServiceState.Stopped)
   { // Service has stopped running; disable the stop and restart button and activate the start button
      notifyIcon.ContextMenu.MenuItems[0].MenuItems.Add(new MenuItem("Start service", new EventHandler(delegate { HostServices.Start(); })) { Index = 0, Enabled = true });
      notifyIcon.ContextMenu.MenuItems[0].MenuItems.Add(new MenuItem("Stop service", new EventHandler(delegate { HostServices.Stop(); })) { Index = 0, Enabled = false });
      notifyIcon.ContextMenu.MenuItems[0].MenuItems.Add(new MenuItem("Restart service", new EventHandler(delegate { HostServices.Restart(); })) { Index = 0, Enabled = false })};
   }
}

Here's the problem: when I call ChangeNotifyIconState(CurrentServiceState.Running), the required MenuItems of the ContextMenu is not changed/updated. Can someone help me get rid of this unexpected behavior and explain me the cause behind it?

Thanks in advance.

EDITS:

I even marked the NotifyIcon object as a volatile object as I speculated that this was because of compiler caching. Turns out it wasn't as making the object volatile didn't solve the issue at hand.

Upvotes: 1

Views: 1252

Answers (1)

Samvel Petrosov
Samvel Petrosov

Reputation: 7706

Here is example how you can create application without form and with NotifyIcon and how you can change menuContextItems of NotifyIcon for example on click event:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApp5
{
    static class Program
    {

        private static Random rd = new Random();
        private static NotifyIcon notifyIcon;
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            NotifyIcon ni = new NotifyIcon(new System.ComponentModel.Container());
            ContextMenu cm = new ContextMenu();

            cm.MenuItems.Add(new MenuItem("First", Click));
            cm.MenuItems.Add(new MenuItem("Second", Click));
            cm.MenuItems.Add(new MenuItem("Third", Click));
            cm.MenuItems.Add(new MenuItem("Exit", (obj,e)=> { Application.Exit(); }));
            ni.Icon = new Icon(@"c:\Users\Admin\Desktop\img.ico");
            ni.Text = "Form1 (NotifyIcon example)";
            ni.ContextMenu = cm;
            ni.Visible = true;
            notifyIcon = ni;
            Application.Run();

        }

        public static bool  SomeCondition()
        {
            return (rd.Next(0, 9999) % 2) == 1;
        }

        public static void Click(object sender, EventArgs e)
        {
            if (SomeCondition())
            {
                ContextMenu cm = new ContextMenu();
                cm.MenuItems.Add(new MenuItem("Fourth", Click));
                cm.MenuItems.Add(new MenuItem("Fifth", Click));
                cm.MenuItems.Add(new MenuItem("Sixth", Click));
                cm.MenuItems.Add(new MenuItem("Exit", (obj, args) => { Application.Exit(); }));
                notifyIcon.ContextMenu = cm;
            }
            else
            {
                ContextMenu cm = new ContextMenu();
                cm.MenuItems.Add(new MenuItem("Third", Click));
                cm.MenuItems.Add(new MenuItem("Sixth", Click));
                cm.MenuItems.Add(new MenuItem("Fourth", Click));
                cm.MenuItems.Add(new MenuItem("Exit", (obj, args) => { Application.Exit(); }));
                notifyIcon.ContextMenu = cm;
            }
        }
    }
}

And here are screenshots how this will be:

At application start the notifyIcon will appear and menu will be the following: enter image description here

As my condition is returning random true or false after click on any menuItem except "Exit" will work Click event handler and change menucontext. As a result you will get menu like one of this: enter image description here

or

enter image description here

Upvotes: 1

Related Questions