Reputation: 632
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
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:
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:
or
Upvotes: 1