"Minimize to tray" is a feature in some applications where minimizing the application removes its taskbar button and replaces it with a (much smaller) notification area icon. Clicking that notification icon restores the application's window - just like clicking its taskbar button would have done.
Well, I was considering using this functionality in a project I'm working on, so I looked to see what my options were. This feature didn't seem to be directly supported by WPF, so I searched the web a bit. What I found after a minute or two of searching was plenty of questions about how to implement this, a few suggestions, and not a lot else. So I figured I'd put something together myself and post it to my blog...
Here's the sample application I'm about minimize - note the (boring) green square of an application icon:
And here's what things look like just after the application is minimized to the tray:
Aside from just looking cool, that notification bubble serves an important purpose: it helps to draw the user's attention to the new icon in the notification area and calls out the application's custom minimize behavior. (To avoid being annoying, the bubble is only shown the first time the application is minimized each time it's run.) This is a nice convenience on most versions of Windows, but is pretty much necessary due to the new Windows 7 behavior of hiding notification area icons as quickly as possible. Without a helpful indication like this bubble, users might "lose" the application when it minimizes to the tray.
Aside: I understand why the Windows 7 team introduced this new behavior and I think it's perfectly reasonable. However, it has implications for this scenario, so it's good to think twice (or thrice!) about choosing to use "minimize to tray" in your application. The document I link to above has lots more guidance on the proper use of notification icons - interested parties are encouraged to review it!
After Windows 7 hides the notification icon for the application, it can be accessed via the "Show hidden icons" popup. Clicking on the hidden notification icon restores the application just like you'd expect:
Aside: If you really dislike the new hiding behavior, it's easy to disable - just click the "Customize..." link (shown in the image above) and you'll be presented with a window that lets you disable this behavior for specific applications - or disable it for all of them with a single checkbox.
Notes:
As a final favor, I'll ask that people please use "minimize to tray" wisely - just because you can minimize your application to the tray doesn't mean you should. :) Minimizing to the tray is something that's only meaningful for a limited set of scenarios - but if you find yourself in one of them, I hope MinimizeToTray is helpful!
[Click here to download the sample application and complete source code for MinimizeToTray.]
The code is quite straightforward - here it is in its entirety:
/// <summary>
/// Class implementing support for "minimize to tray" functionality.
/// </summary>
public static class MinimizeToTray
{
/// <summary>
/// Enables "minimize to tray" behavior for the specified Window.
/// </summary>
/// <param name="window">Window to enable the behavior for.</param>
public static void Enable(Window window)
{
// No need to track this instance; its event handlers will keep it alive
new MinimizeToTrayInstance(window);
}
/// <summary>
/// Class implementing "minimize to tray" functionality for a Window instance.
/// </summary>
private class MinimizeToTrayInstance
{
private Window _window;
private NotifyIcon _notifyIcon;
private bool _balloonShown;
/// <summary>
/// Initializes a new instance of the MinimizeToTrayInstance class.
/// </summary>
/// <param name="window">Window instance to attach to.</param>
public MinimizeToTrayInstance(Window window)
{
Debug.Assert(window != null, "window parameter is null.");
_window = window;
_window.StateChanged += new EventHandler(HandleStateChanged);
}
/// <summary>
/// Handles the Window's StateChanged event.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="e">Event arguments.</param>
private void HandleStateChanged(object sender, EventArgs e)
{
if (_notifyIcon == null)
{
// Initialize NotifyIcon instance "on demand"
_notifyIcon = new NotifyIcon();
_notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetEntryAssembly().Location);
_notifyIcon.MouseClick += new MouseEventHandler(HandleNotifyIconOrBalloonClicked);
_notifyIcon.BalloonTipClicked += new EventHandler(HandleNotifyIconOrBalloonClicked);
}
// Update copy of Window Title in case it has changed
_notifyIcon.Text = _window.Title;
// Show/hide Window and NotifyIcon
var minimized = (_window.WindowState == WindowState.Minimized);
_window.ShowInTaskbar = !minimized;
_notifyIcon.Visible = minimized;
if (minimized && !_balloonShown)
{
// If this is the first time minimizing to the tray, show the user what happened
_notifyIcon.ShowBalloonTip(1000, null, _window.Title, ToolTipIcon.None);
_balloonShown = true;
}
}
/// <summary>
/// Handles a click on the notify icon or its balloon.
/// </summary>
/// <param name="sender">Event source.</param>
/// <param name="e">Event arguments.</param>
private void HandleNotifyIconOrBalloonClicked(object sender, EventArgs e)
{
// Restore the Window
_window.WindowState = WindowState.Normal;
}
}
}