Click here to Skip to main content
16,015,504 members
Articles / Desktop Programming / Win32
Tip/Trick

Doing a NotifyIcon Program the Right Way

Rate me:
Please Sign up or sign in to vote.
4.92/5 (26 votes)
29 Jul 2013CPOL3 min read 140.5K   5.5K   50   16
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:

C#
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:

C#
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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
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 Microsoft Dynamics AX and .Net programmer, and have a number of projects in various states of progress to work on in the spare time...

Comments and Discussions

 
QuestionHow can we stop notifications from persisting in the action center ? Pin
Member 1322561017-Oct-19 0:08
Member 1322561017-Oct-19 0:08 
QuestionNo need for disposal code? Pin
jectster28-Jun-19 9:05
jectster28-Jun-19 9:05 
QuestionAnother simpler solution Pin
sakumira24-Feb-19 12:41
sakumira24-Feb-19 12:41 
QuestionShow active form on click Pin
Member 126180832-Nov-17 11:24
Member 126180832-Nov-17 11:24 
AnswerRe: Show active form on click Pin
Johnny J.3-Nov-17 4:00
professionalJohnny J.3-Nov-17 4:00 
QuestionHow to keep notification? Pin
Rafael Ferreira6-May-17 4:32
Rafael Ferreira6-May-17 4:32 
AnswerRe: How to keep notification? Pin
Johnny J.7-May-17 20:00
professionalJohnny J.7-May-17 20:00 
QuestionWell and good!! Pin
karthik cad/cam2-Nov-14 22:27
karthik cad/cam2-Nov-14 22:27 
QuestionA neat solution Pin
George Swan29-Jul-13 20:20
mveGeorge Swan29-Jul-13 20:20 
Question"The right way" Pin
dandy7229-Jul-13 7:35
dandy7229-Jul-13 7:35 
AnswerRe: "The right way" Pin
Johnny J.29-Jul-13 19:42
professionalJohnny J.29-Jul-13 19:42 
GeneralRe: "The right way" Pin
dandy7230-Jul-13 14:43
dandy7230-Jul-13 14:43 
GeneralRe: "The right way" Pin
Noobs R Us21-Jul-16 17:05
professionalNoobs R Us21-Jul-16 17:05 
GeneralRe: "The right way" Pin
Johnny J.21-Jul-16 22:43
professionalJohnny J.21-Jul-16 22:43 
AnswerRe: "The right way" Pin
Richard Deeming12-Aug-13 8:03
mveRichard Deeming12-Aug-13 8:03 
GeneralMy vote of 5 Pin
rht34129-Jul-13 2:46
rht34129-Jul-13 2:46 

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

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