Click here to Skip to main content
Click here to Skip to main content

Doing a NotifyIcon program the right way

, 29 Jul 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
How to do a program whose main graphic interface consists of an icon in the system tray

Introduction

Some times 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... Wink | ;-)

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

  • Version 1.00 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Johnny J.
Software Developer (Senior)
Sweden Sweden
Born in Copenhagen, Denmark
Have been living in Paris, France and L.A., The United States
Now live in Stockholm, Sweden
 
Started programming when I got my first VIC 20, and a few months later on Commodore 64. Those were the days!
 
Studied programming at the Copenhagen Engineering Academy
 
Professional console, winforms and webforms programming in Comal, x86 Assembler, Fortran, Pascal, Delphi, Visual Basic 3 through 6, Classic ASP, C# and VB.NET
 
I now work as Senior .NET developer building Airline Booking Systems, and have a number of projects in various states of progress to work on in the spare time...
 
PS: The cat on my profile is one of my three cats, Ramses. He's all white, odd-eyed, deaf and definitely the coolest cat there is!

Comments and Discussions

 
QuestionWell and good!! Pinmemberkarthik cad/cam2-Nov-14 23:27 
QuestionA neat solution PinmemberGeorge Swan29-Jul-13 21:20 
Question"The right way" Pinmemberdandy7229-Jul-13 8:35 
AnswerRe: "The right way" PinprofessionalJohnny J.29-Jul-13 20:42 
GeneralRe: "The right way" Pinmemberdandy7230-Jul-13 15:43 
AnswerRe: "The right way" PinprofessionalRichard Deeming12-Aug-13 9:03 
GeneralMy vote of 5 Pinmemberrht34129-Jul-13 3:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 29 Jul 2013
Article Copyright 2013 by Johnny J.
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid