Geeks With Blogs
Willem's... {rue if I mellow}

[ Comment: This code was originally written for .NET 1.1 and ported as is. As such, it should not refer to .NET 2.0 at all. Please refer to the comments of Stefan Els below for a 'real' and more elegant .NET 2.0 implementation ]

Providing a basic 'system tray application' using VS2005/C#/.NET 2.0 is very simple.

The functionality is this:

  • .NET Windows Forms application.
  • When the main window is closed, the application is not terminated - instead the main window is not visible and an icon appears in the system tray (Win XP/XP Server 2003). This can also be extended to provide the same functionality when the window is minimized.
  • The system tray icon provides a context menu with at least a Restore and Exit option.
  • The Restore option reverses the process by making the main window appear again, and removing the system tray icon.
  • The Exit option provides for standard and proper program termination.

The sample code (see below) makes no attempt to restrict the application to a single instance.  That aspect is handled here:

The code below is commented and largely self-explanatory. The following form controls are required:

  • A NotifyIcon control to display and manage the application in the system tray.
  • A ContextMenuStrip control which has to be referenced by the NotifyIcon control, and contains the Restore and Exit menu items.

 

// NotifyIcon control added to the form.
private System.Windows.Forms.NotifyIcon niApp;

..
..

// Flags actual application termination.
private bool _exitApplication = false;
// Response method called when form is requested to close.
protected override void OnClosing(CancelEventArgs e)
{
  // If actual termination is flagged, do it!
  if (_exitApplication == true)
  {
    e.Cancel = false;
  }
  else
  {
    // Not exiting - cancel.
    e.Cancel = true;
    // Instead of exiting - hide form.
    Visible = false;
  }
}
// Windows message constant for system shutdown request.
private const int WM_QUERYENDSESSION = 0x11;
protected override void WndProc(ref Message msg)
{
  if (msg.Msg == WM_QUERYENDSESSION)
  {
    // If system is shutting down, allow exit.
    _exitApplication = true;
  }
  base.WndProc(ref msg);
}
// Response method called when form visibility changes.
protected override void OnVisibleChanged(EventArgs e)
{
  base.OnVisibleChanged(e);
  // Change visibility of tray icon - opposite to forms visibility.
  niApp.Visible = Visible ? false : true;
}
// Context menu Restore option response method.
private void tsmRestore_Click(object sender, EventArgs e)
{
  // Show form.
  Visible = true;
}
// context menu Exit option response method.
private void tsmExit_Click(object sender, EventArgs e)
{
  // Ensure window close.
  _exitApplication = true;
  // Exit the application.
  Application.Exit();
}
Posted on Monday, January 30, 2006 7:22 AM .NET Adventures | Back to top


Comments on this post: System tray application basics for .NET 2.0

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
Thanks! This is much more elegant than the method I was using. Mine was working but the form would flicker when it closed. Also, I need the system tray icon visible all the time, so I commented out the line

//niApp.Visible = Visible ? false : true;
Left by Max Irwin on Mar 23, 2006 8:39 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
This is not an elegant solution. Keep it simple and use the events on the form - no reason to override the form's OnClosing() or anything else.

Also, simplify use of booleans, so instead of:
niApp.Visible = Visible ? false : true;
use the obvious and proper:
niApp.Visible = !Visible;

So, instead of statements like:
if (boolean1 == true) boolean2 = false;
if (boolean1 == true) doSomething();
rather use:
boolean2 = !boolean1;
if (boolean1) doSomething();

Also, avoid defining cryptic constants like WM_QUERYENDSESSION in your example above. Not only are values like this exposed by the C# system classes but you should avoid overriding the WndProc() method. There is a much easier way (the proper way) to determine whether Windows is shutting down or not. Using WndProc() to do this is way over the top and reminds me of unfriendly C++ code of five years ago. You are supposed to use the FormClosingEventArgs provided by the form's FormClosing() event. Everything you need to know about the form's closing down is provided to you by C# in the relevant place: the FormClosing() method. You are re-inventing the wheel here and also creating difficult to read code that is unnecessarily convoluted. All this can be done in so many fewer lines of clearer code.

See these principles at work in this cleaner FormClosing() event:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
// Only close app if a "close" menu item chosen or
// Windows is shutting down.
bool leaveApp = (exitApplication || (e.CloseReason == CloseReason.WindowsShutDown));
e.Cancel = !leaveApp;
Visible = leaveApp; // If not leaving hide form instead.
}

private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
exitApplication = true; // Really do want to close app and not merely hide it.
Application.Exit();
}

private void restoreToolStripMenuItem_Click(object sender, EventArgs e)
{
Visible = true; // Restore form to sight.
}

Here, exitApplication can be set by the tray icon context menu or even a main menu "exit" to enable the behaviour that the user can close the app by choosing an "exit" menu item from the tray icon popup menu or the app's main menu, but the app will not close if the close "X" button is clicked or the system menu's "close" item is clicked. The app also closes if Windows is shutting down.
Left by Stefan Els on Oct 08, 2007 5:45 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
Hi Stefan, you're absolutely right...in most cases it is inappropriate to use constructs and overrides that are applicable to the C++/MFC/win32 world, especially if .NET/C#/VB functional equivalent constructs can be used. Additionally, your code is definitely more readable.

One comment though for the readers - the 'FormClosing' event is only available in .NET 2.0+, and is not applicable to this particular bit of code which was originally written during the days of .NET 1.1.
Left by Willem on Oct 08, 2007 6:57 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
Wow, I did not realize .NET 1.1 did not include a FormClosing() event!

My minor quibbles on boolean use aside I must say your example here got me up and running with my first notify tray app within a couple of minutes - well done. Some other examples I trawled for via google were not so clear and simple in demonstrating the bare bones elements needed, so thanks for your effort in putting this example together.
Left by Stefan Els on Oct 08, 2007 10:30 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
Thanks for the example Willem.

Just to let you know, the .NET 2.0 FormClosing event is more or less the same as the .NET 1.1 Closing event, which will allow you to stop the window from closing by modifying the event arguments, as demonstrated by Stefan.

Here's the MSDN page for the event if you're curious:
http://msdn2.microsoft.com/en-us/library/system.windows.forms.form.closing(VS.71).aspx
Left by James on Jan 28, 2008 11:44 PM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
Good one
Thanks
Left by Tijo on Apr 03, 2009 4:13 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
C# has done a great job with this. 20 second task to create systray apps now.
http://www.itjungles.com/dotnet/easy-steps-to-create-a-system-tray-application-with-c
Left by Ben on Apr 30, 2010 3:24 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
hey i have created a tray icon for my windows service ,it wrks well but i have a problem that not being solved..the problem is when i minimize normal sized window form ,it goes towards left of the dekstop below ,just above the taskbar..the title bar with maximize,cancel is seen..i dnt want that to happen.

i want it the icon only..and work from there...what do i do???waiting for response..

thanks
Mumtaz
Left by Mumtaz on Sep 17, 2010 6:01 AM

# re: System tray application basics for .NET 2.0
Requesting Gravatar...
Hi Mumtaz,

this will catch the minimise event
private void Form1_Resize(object sender, EventArgs e)
{
if (FormWindowState.Minimized == WindowState)
{
Hide();
}
}

Left by MGB on Sep 22, 2010 9:10 AM

Your comment:
 (will show your gravatar)


Copyright © Willem Fourie | Powered by: GeeksWithBlogs.net | Join free