Minimizing a window to the System Tray on a Close event

A question that .Net programmers does frequently ask is the following : how to minimize a window in the systray when the user clicks the "Close" button ? This post exposes solutions to the various problems this quite simple need implies.

The first step is to prevent the window to close. This can be done either by handling the Closing event or by overriding the OnClosing protected virtual method. For performance and memory consumption reasons, I'd prefer not to use event handlers whenever possible. The code is quite simple since whe only need to cancel the operation, and looks like the following...

Code Copy
/// <summary>
///
Occurs when the window is requested to be closed.
/// </summary>
///
<param name="e">The event arguments</param>
protected override void OnClosing(CancelEventArgs e)
{
// The window must only be minimized in tray
e.Cancel = true;
MinimizeInTray();

base.OnClosing(e);
}

... and the MinimizeInTray method must do the following:

  • Minimize the window,
  • Hide the window item in the taskbar.
Code Copy
private void MinimizeInTray()
{
this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized;
}

Now, supposing that you have a context menu attached to the NotifyIcon object that represents the systray icon for your application, you can add the following menu items:

  • An openMenu menu item that restores the window,
Code Copy
private void openMenu_Click(object sender, System.EventArgs e)
{
ShowFromTray();
}

private void ShowFromTray()
{
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;
}
  • And an exitMenu item that closes the application.
Code Copy
private void exitMenu_Click(object sender, System.EventArgs e)
{
Application.Exit();
}

Note that calling Close() fails because our OnClosing method prevents the window to close...

Now, we should think that our job is completed. But (yes, there's always a but in programming), what happens if the computer is being shutdown ? The answer is quite simple : our OnClosing implementation prevents the window to be closed, which prevents the computer to be shutdown...

To solve this issue, we need some lightweight Interop :

  • First, override the WndProc virtual method to handle the WM_QUERYENDSESSION message:
Code Copy
private const int WM_QUERYENDSESSION = 0x11;
private bool endSessionPending;

protected
override void WndProc(ref Message m)
{
if (m.Msg == WM_QUERYENDSESSION)
endSessionPending = true;
base.WndProc(ref m);
}
  • Then, modify the OnClosing method to handle the event in a different way :
Code Copy HideScrollFull
/// <summary>
///
Occurs when the window is requested to be closed.
/// </summary>
///
<param name="e">The event arguments</param>
protected override void OnClosing(CancelEventArgs e)
{
if (endSessionPending)
{
// The session is ending.
e.Cancel = false;
}
else
{
// The window must only be minimized in tray
e.Cancel = true;
MinimizeInTray();
}

base.OnClosing(e);
}
. . .

And that's all!

The full C# sample code is available below. There's only missing icon resources, but you will correct them by yourself!

Code Copy HideScrollFull
#region References

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Security.Permissions;

#endregion

namespace
MinimizeOnClose
{
/// <summary>
/// Represents the main window of the application.
/// </summary>
public class MinimizeOnClose : System.Windows.Forms.Form
{
#region Interop Constants

private const int WM_QUERYENDSESSION = 0x11;

#endregion

#region
Fields

private System.Windows.Forms.NotifyIcon notifyIcon;
private System.Windows.Forms.ContextMenu notifyIconMenu;
private System.Windows.Forms.MenuItem openMenu;
private System.Windows.Forms.MenuItem hideMenu;
private System.Windows.Forms.MenuItem exitMenu;
private System.ComponentModel.IContainer components;
private System.Windows.Forms.MenuItem blankMenu;

private bool endSessionPending;

#endregion

#region
Instance Management

public MinimizeOnClose()
{
InitializeComponent();
}

#endregion

#region
Protected Overrides

/// <summary>
/// Cleans all the resources.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}

/// <summary>
/// Occurs when the window is requested to be closed.
/// </summary>
/// <param name="e">The event arguments</param>
protected override void OnClosing(CancelEventArgs e)
{
if (endSessionPending)
{
// The session is ending
e.Cancel = false;
}
else
{
// The window must only be minimized in tray
e.Cancel = true;
MinimizeInTray();
}

base.OnClosing(e);
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_QUERYENDSESSION)
endSessionPending = true;
base.WndProc(ref m);
}
#endregion
#region Private Members

#region Event Handlers

private void openMenu_Click(object sender, System.EventArgs e)
{
ShowFromTray();
}

private void hideMenu_Click(object sender, System.EventArgs e)
{
MinimizeInTray();
}

private void exitMenu_Click(object sender, System.EventArgs e)
{
Application.Exit();
}

#endregion

#region
Private Methods

private void MinimizeInTray()
{
this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized;

openMenu.Visible = true;
hideMenu.Visible = false;
}

private void ShowFromTray()
{
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;

openMenu.Visible = true;
hideMenu.Visible = false;
}

#endregion

#region
Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(MinimizeOnClose));
this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.notifyIconMenu = new System.Windows.Forms.ContextMenu();
this.openMenu = new System.Windows.Forms.MenuItem();
this.hideMenu = new System.Windows.Forms.MenuItem();
this.blankMenu = new System.Windows.Forms.MenuItem();
this.exitMenu = new System.Windows.Forms.MenuItem();
//
// notifyIcon
//
this.notifyIcon.ContextMenu = this.notifyIconMenu;
this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon")));
this.notifyIcon.Text = "MinimizeOnClose";
this.notifyIcon.Visible = true;
//
// notifyIconMenu
//
this.notifyIconMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.openMenu,
this.hideMenu,
this.blankMenu,
this.exitMenu});
//
// openMenu
//
this.openMenu.Index = 0;
this.openMenu.Text = "&Open";
this.openMenu.Visible = false;
this.openMenu.Click += new System.EventHandler(this.openMenu_Click);
//
// hideMenu
//
this.hideMenu.Index = 1;
this.hideMenu.Text = "&Hide";
this.hideMenu.Click += new System.EventHandler(this.hideMenu_Click);
//
// blankMenu
//
this.blankMenu.Index = 2;
this.blankMenu.Text = "-";
//
// exitMenu
//
this.exitMenu.Index = 3;
this.exitMenu.Text = "E&xit";
this.exitMenu.Click += new System.EventHandler(this.exitMenu_Click);
//
// MinimizeOnClose
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(240, 78);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "MinimizeOnClose";
this.Text = "MinimizeOnClose";
}
#endregion

/// <summary>
/// The application entry point.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MinimizeOnClose());
}

#endregion
}
}
. . .

Have a nice week-end, and do not overindulge in Easter eggs too much!

posted on Saturday, March 26, 2005 12:02 PM

Feedback

# re: Minimizing a window to the System Tray on a Close event 4/15/2005 6:51 PM Raghu

Hello,

THis works well. Thanks for the snippet...

I have one question regarding this though...

HOw can I show a bubble message that 'MyApp is still running in the background!' as soon as the application gets minimized to the tray??

Thanks.

Raghu.

# re: Minimizing a window to the System Tray on a Close event 4/18/2005 7:42 PM Skup

Showing a balloon tip on a notify icon can be done using the Shell API function Shell_NotifyIcon with flags NIF_INFO | NIF_ICON | NIF_MESSAGE.

The problem is that you need the notify icon message window handle and the notify icon id to call the function, and the WinForm Notify Icon class does not give access to it. So you have to rewrite all that code.

I'll give a implementation of that soon since it is the only way to make ownerdrawn menu work correctly on a notify icon.

If you cannot wait, have a look at MS implementation using Reflector (there's a link on the left)!

# re: Minimizing a window to the System Tray on a Close event 1/14/2008 2:39 PM prem

hi
thanks for this document.

i want my tray application get started as the system starts. but not as a windows service. how can i do that?
thanks in advance

# re: Minimizing a window to the System Tray on a Close event 7/20/2008 8:46 AM Aron

It is the year 2019, 30 years after the first AKIRA project led to the destruction of Tokyo and the start of World War III. The original AKIRA project was a secret experiment to develop a new form of human evolution through the manipulation of the abilities and powers of psychically gifted children. The military hoped to use the children as living weapons, while the scientists had hoped to develop a new genetically superior human being.

But both the military and anime scientists involved in the project learned too late that the power they were seeking could not be controlled. Akira, one of the children involved in the experiment, developed into a force so great, the he literally destroyed everything about him through a terrifying burst of psychic energy, setting off a nuclear-like explosion which led to the world war.
http://sig-ment.com/index.php?id=114
http://mistoru.com/index.php?id=66
http://vistorg.com/index.php?id=172
Now, 30 years later, the military and scientific communities decide to revive the AKIRA project, deluded by narrow-mindedness into thinking they could control a power their predecessors could not.

Title
 
Name
 
Url
Comments   
Protected by Clearscreen.SharpHIPEnter the code you see: