Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Doing a NotifyIcon Program the Right Way

0.00/5 (No votes)
29 Jul 2013 1  
How to do a program whose main graphic interface consists of an icon in system tray
In this tip, you will learn to make a program whose main graphic interface consists of an icon in the system tray.

Introduction

Sometimes, when you do a desktop program, you want to display an icon in the system tray to be able to display some information to the user. Luckily, Microsoft has made this very easy for us developers by supplying us with the NotifyIcon component. You can simply drop an instance of that on your form and presto! You've got easy access to the system tray right away - no coding needed!

But what happens if you want to do a program whose only user interface consists of the tray icon? You don't want to have a form to put it on!

In "the old days" (before VS.NET) when I worked a lot with Visual Basic 3 through 6, I would have created the form anyway, put the component on it, and have tried to hide the form. The way you would normally do that was to move it outside of the visual desktop area by setting the forms StartPosition to Manual and then setting the Top and Left properties to some ridiculously large negative number in the forms Load routine.

I have always thought that doing that seemed like an unnecessary hack, and I still do that.

Here's How!

So how do you do it correctly in .NET? The "old way" still works of course, but why would I want to do that when I don't like the approach? And there is a better way of doing it!

If you look at the Program.cs file in your WinForms project, you will see that it looks something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TrayIconTest
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

When the application is run, it creates an instance of your form and runs it. And that is exactly what you don't want in this specific case.

You could try to put all your code in the Main method of the Program class and skip the call to the Application.Run method, but that is not a good idea, and it doesn't work either.

But here's the thing: the Application.Run method doesn't need to have a Form parameter. It will accept an ApplicationContext object instead. And that is what we need to do here.

We create a class and name it as we like (I call it MyApplicationContext), and we make sure to derive it from the .NET ApplicationContext object. Then we use that in our Run method call instead of Form1 like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TrayIconTest
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyApplicationContext());
        }
    }
} 

So now we can put our NotifyIcon code in our MyAccplicationContext class instead and not have to worry about hiding a Form.

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

namespace TrayIconTest
{
    class MyApplicationContext : ApplicationContext
    {
        //Component declarations
        private NotifyIcon TrayIcon;
        private ContextMenuStrip TrayIconContextMenu;
        private ToolStripMenuItem CloseMenuItem;

        public MyApplicationContext()
        {
            Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
            InitializeComponent();
            TrayIcon.Visible = true;
        }

        private void InitializeComponent()
        {
            TrayIcon = new NotifyIcon();

            TrayIcon.BalloonTipIcon = ToolTipIcon.Info;
            TrayIcon.BalloonTipText = 
              "I noticed that you double-clicked me! What can I do for you?";
            TrayIcon.BalloonTipTitle = "You called Master?";
            TrayIcon.Text = "My fabulous tray icon demo application";
            

            //The icon is added to the project resources.
            //Here, I assume that the name of the file is 'TrayIcon.ico'
            TrayIcon.Icon = Properties.Resources.TrayIcon;

            //Optional - handle doubleclicks on the icon:
            TrayIcon.DoubleClick += TrayIcon_DoubleClick;

            //Optional - Add a context menu to the TrayIcon:
            TrayIconContextMenu = new ContextMenuStrip();
            CloseMenuItem = new ToolStripMenuItem();
            TrayIconContextMenu.SuspendLayout();

            // 
            // TrayIconContextMenu
            // 
            this.TrayIconContextMenu.Items.AddRange(new ToolStripItem[] {
            this.CloseMenuItem});
            this.TrayIconContextMenu.Name = "TrayIconContextMenu";
            this.TrayIconContextMenu.Size = new Size(153, 70);
            // 
            // CloseMenuItem
            // 
            this.CloseMenuItem.Name = "CloseMenuItem";
            this.CloseMenuItem.Size = new Size(152, 22);
            this.CloseMenuItem.Text = "Close the tray icon program";
            this.CloseMenuItem.Click += new EventHandler(this.CloseMenuItem_Click);

            TrayIconContextMenu.ResumeLayout(false);
            TrayIcon.ContextMenuStrip = TrayIconContextMenu;
        }

        private void OnApplicationExit(object sender, EventArgs e)
        {
            //Cleanup so that the icon will be removed when the application is closed
            TrayIcon.Visible = false;
        }

        private void TrayIcon_DoubleClick(object sender, EventArgs e)
        {
            //Here, you can do stuff if the tray icon is doubleclicked
            TrayIcon.ShowBalloonTip(10000);
        }

        private void CloseMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Do you really want to close me?",
                    "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button2) == DialogResult.Yes)
            {
                Application.Exit();
            }
        }
    }
}  

Normally, when you drop a NotifyIcon component on a Form, you don't need to worry any more about that, the designer handles all the coding and puts it in the designer file. In this case, you don't have a designer file, so you have to create the controls manually in your code. You could (if you wanted to) create a designer file yourself as a partial file and move the InitializeComponent method to that, but for this example, that would be overkill in my opinion.

Voila! That's all there is to it. A WinForms application without any WinForms... ;-)

You can of course create forms in your projects and show them, if you e.g., need a settings form or the like. That part works just as usual.

An alternative would of course be to create the application as a Windows Service instead, but that adds a lot more complexity to the project and requires that you also do an installer project. In some cases, that is way too much work for such a small task.

History

  • 29th July, 2013: Version 1.00 - Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here