Browsing Posts in WinForms

With Windows Forms 2.0 and a bit of ingenuity, you can easily make your application minimize to the system tray – that little area to the left of your clock. So let’s see how it’s done…


First stop – the NotifyIcon. The NotifyIcon is a new widget in Windows Forms 2.0, which displays a tray icon for your form. Simply drop a NotifyIcon on your form, set its Icon property to an .ico file of your choice, and set its Visible property to false. (I’m assuming that you want to either display the tray icon or your application, but not both.)


So far, so good. Now we want to find out when the form is minimized. We do this by finding out when the form is resized and examining its current WindowState, which we’ll do by overriding the OnResize method. (Overriding the OnXxx() methods is the preferred method for handling these types of Windows events if you’re creating a derived class since you don’t incur the overhead of a multicast delegate. If you want to capture these events from outside the form, use the appropriate event, such as Resize in our case.) If your WindowsState is minimized, display your tray icon and hide your form.


Now when we double-click our tray icon, we want to reverse the process by hiding the tray icon and displaying the form again. We do this by restoring the previous WindowState, but how do we know what the previous Window state was. We better keep track of it. Whenever a form is minimized, maximized, or restored, OnResize is called (and the Resize event is fired). So as long as the current WindowState isn’t minimized, we squirrel away the current value in a member variable. We then know what to restore the form to when the tray icon is double-clicked. There’s some additional logic in the constructor to set our restore state to a sane value when the application starts up.


That about wraps it up. You can download the sample code MinimizeToTray.zip (22.08 KB), though most is below for your viewing pleasure. Enjoy!

using System;
using System.Windows.Forms;

namespace JamesKovacs.Samples.MinimizeToTray {
public partial class Form1 : Form {
private FormWindowState m_previousWindowState;

public Form1() {
InitializeComponent();
// Store the initial window state that we want to restore to when we double-click the tray icon
m_previousWindowState = (this.WindowState == FormWindowState.Minimized ? FormWindowState.Normal : this.WindowState);
}

protected override void OnResize(EventArgs e) {
base.OnResize(e);
// We need to keep track of whether you’re minimizing from a normal or maximized window
if(this.WindowState != FormWindowState.Minimized) {
m_previousWindowState
= this.WindowState;
}
notifyIcon1.Visible
= (this.WindowState == FormWindowState.Minimized);
this.Visible = !notifyIcon1.Visible;
}

private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) {
this.Visible = true;
notifyIcon1.Visible
= false;
this.WindowState = m_previousWindowState;
}
}
}

Another thing that’s been kicking around for awhile and I figured it was time to persist to long-term storage. Here are the three top-level places in a WinForms app where you absolutely need exception handling routines to ensure that all exceptions are caught:



  1. try/catch around Main()
  2. Attach a System.Threading.ThreadExceptionEventHandler to Application.ThreadException
  3. Attach a UnhandledExceptionEventHandler to AppDomain.CurrentDomain.UnhandledException

Note that simply having a try/catch around Main() is insufficient as you will not catch exceptions that occur during the execution of event handlers or asynchronous callbacks.

I’ll be presenting a series of talks for Calgary .NET User Group on acquiring your MCAD/MCSD. The series will include test taking strategies, helpful hints, and other useful information you should know about attaining your credentials.


The first in the series will be Tips & Tricks for 70-306/70-316: Developing Windows®-Based Applications.


Date & Time: Wednesday, September 14, 2005, 5:00 PM to 6:30 PM (Registration starts at 4:30pm)
Location: Alberta EUB Building, Training Room-2nd Floor, 640 5th Avenue SW, Calgary, AB


Pre-registration at the user group website is required.


Thanks to everyone who attended. You can find the slidedeck here and the VB.NET polymorphism demo here.

From John Montgomery (via Chris Sells), we finally have some recommendations from Redmond on which UI technology we should use going into the future. The basic message - use common sense. Use WinForms now while Avalon is in development. Consider Avalon for appropriate scenarios when it debuts. Use Avalon (and re-use previously developed WinForms controls via interop) once Visual Studio supports Avalon development.