Sunday 25 July 2021

Tray icon management in Windows Forms C# app

Today something different. The problem I’ll write about is: how to make a Windows Forms (C#) app minimize to tray when cliked on „X” and prevent it from displaying in the taskbar.

In other words – how to make a „typical tray app”.

To be honest – I was quite sure there would be some component (control) for it.

And there is (NotifyIcon) but… It’s not enough just to drag it to our app designer form in Visual Studio to make it work as we would like to.

Let’s begin with opening Visual Studio and creating a new Windows Forms App:

File -> New -> Project -> Windows Forms Apps -> Ok

First, wee drag NotifyIcon component from Toolbox to our form (it will appear below it, as it’s not visible in the form area, but outside it)



And here is the first strange behaviour: if we run (F5 or Ctrl+F5, but i’d suggest F5 as it allows it to kill the app if something goes wrong, and it can in this case) our app, the NotifyIcon component doesn’t seem to work. The tray icon doesn’t show up, and neither minimization nor clicking the „X” button causes our app to minimize to tray.

That’s because our tray icon is not set and we haven't implemented minimization by clicking "X". First the icon: in order to set it, we need the tray icon itself :)

We can just use Paint to create it – set the canvas size to 16x16 (this is the standard tray icon size in Windows), draw something beautiful and save it as .jpg file. Now rename it to something.ico – simply change the extension! The ico files are simply graphics files with a particular extension.

Now you can copy it to project – you can just drag it to project name in Solution Explorer view in VS.



It should show up below (myicon.ico) in the image above.

Now if you hit F5, the icon should show in the tray :)

The problem is it doesn’t do anything. The app still minimizes to the taskbar.

So let’s edit the code. Double click on the form project appearing in VS. The form code should appear, and it should be similar to this:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object senderEventArgs e)
        {

        }
    }
}

 

First of all, let’s make our icon actually do something: it should show form if clicked twice, and hide it if clicked twice again. In order to do it, select our NotifyIcon component in the designer (Form1.cs [ Design] tab):


Now select the lightning icon in Properties windows (F4 if the window is not visible), click the empty area on the right of „MouseDoubleClick” and then double click on it again :)



 An empty method should appear in the editor:


private void notifyIcon1_MouseDoubleClick(object senderMouseEventArgs e)
{

}

Let's implement it:


private void notifyIcon1_MouseDoubleClick(object senderMouseEventArgs e)
{
    if (this.Visible == true)
    {
        this.Hide();
    }
    else
    {
        this.Show();
    }
}

Now when you run the app and double click on the tray icon, the window should disappear. Double click on the icon again, and the window should appear. Now close the window – let’s get back to the code: we’ll add a context (right click) menu to the tray icon.

The remaining problems are as follows: the application exits, when we click „X” in the top bar. This is correct behaviour for most apps, but those enabled on tray should keep working even if „X” is clicked (the typical way of closing them is to right click on the tray icon and choosing „Close” or something similar from the context menu). The second problem is: the application still appears on the „normal” taskbar, and we want it to be minimized only to tray.

To fix the first issue, modify the Form1 constructor in this way:  


public Form1()
{
    InitializeComponent();
    this.FormClosing += this.Form1_FormClosing;
}

 … and add the callback for FormClosing event below:


private void Form1_FormClosing(object senderFormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing)
    {
        this.Hide();
        e.Cancel = true;
    }
}

If you want to run the app now, do it using F5 (not Ctrl+F5) – you’ll need to terminate it using „Stop Debugging” (red square button or Shift+F5 in Visual Studio). If you forget about it, you’ll have to close it via the task manager :)

Now let’s add a possibility of closing the app via the context menu for the tray icon. In order to do that, switch to the Form1.cs [Design] view and drag ContextMenuStrip component to your designer view (window).



Now click on the "Type Here” area, type "Exit” and double click on it. It should take you to the editor, with generated code stub:


private void exitToolStripMenuItem_Click(object senderEventArgs e)
{

}

We want to close app when choosing ‘Exit’ option, so implement like this:


private void exitToolStripMenuItem_Click(object senderEventArgs e)
{
    Application.Exit();
}

Of course, you can add more items to the menu (and add whatever code you want to it).

The last thing to do is to attach this menu to our tray icon. In order to do that, choose notifyIcon1 from the area below the designer:



 ...and choose our ContextMenuStrip in the Properties window on the right:



When you run the app now and right-click on the tray icon, "Exit” option should appear (and should work).

To last thing to do is to prevent the app from showing on the "normal” taskbar. To do this, add one last line to Form1 constructor:


public Form1()
{
    InitializeComponent();
    this.FormClosing += this.Form1_FormClosing;
    ShowInTaskbar = false;
}

That's all for today - I think I was able to show you a simple stub of Windows app using tray in C#. If you have any questions about this - ask them in comments :)

No comments:

Post a Comment

Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means cre...