Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Close Your Application to the Notification Area

0.00/5 (No votes)
25 Aug 2010 1  
How to write an app that will go to the system tray when the user closes it
Tray.png

Introduction

This article expands on an earlier one of mine, Create a System Tray Application in VB.NET[^]. If you are unfamiliar with how to write applications for the notification area, that might be a good place to start.

Background

I recently got a BitTorrent downloader that does something very interesting: when the application is closed, it switches into a background mode and minimizes to an icon in the notification area. Right clicking the icon gives a menu that shows me how many torrents are still downloading and lets me restore to a regular window or exit the app completely. That seemed like a spiffy piece of functionality, so I set out to learn how I could write applications that do the same thing. This article is the result.

As usual, I wrote this in VB.NET, because that is the language I am most familiar with. It should be pretty easy to translate into C#: just keep in mind that some of the implementation is VB specific.

The code and demo were tested using Visual Studio 2008 and the 3.5 Framework; it should work with .NET 4.0, and probably with 2.0 as well. If not, please leave a comment.

Context is Everything

Effectively, this technique creates a notification area application that may or may not have a visible user interface. We start by creating a derivative of System.Windows.Forms.ApplicationContext:

Public Class AppContext
    Inherits ApplicationContext

#Region " Constructor "

    Public Sub New(ByVal StartClosed As Boolean)
        InitializeApp()

        If StartClosed Then
            Notify.Visible = True
        Else
            AppForm.Show()
        End If
    End Sub

#End Region

#Region " Event handlers "

    Private Sub AppContext_ThreadExit(ByVal sender As Object, _
    ByVal e As System.EventArgs) _
    Handles Me.ThreadExit
        'Guarantees that the icon will not linger.
        Notify.Visible = False
    End Sub

#End Region

End Class

When AppContext is created, it takes a parameter indicating whether the app will start already closed to the notification area. After initializing everything, the application thread launches by either displaying the icon in the system tray or by showing the application's form. When the application thread ends -- it was exited by the user, cancelled in the Program Manager, Windows is shutting down, whatever -- the ThreadExit event gets called, which allows you to do whatever cleanup is necessary, such as closing files and saving the application state. All I am doing here is making sure that the icon has been removed from the notification area.

The Code Behind the Curtain

The next step is to create MainModule with the infrastructure that actually manages the app. Declare the components with the WithEvents keyword so you can intercept the components' events.

#Region " Global storage "

    Public WithEvents AppForm As MainForm
    Public WithEvents Notify As NotifyIcon
    Public WithEvents MainMenu As ContextMenuStrip
    Public WithEvents mnuShow As ToolStripMenuItem
    Public WithEvents mnuSep1 As ToolStripSeparator
    Public WithEvents mnuExit As ToolStripMenuItem

    Public TimerMenu As ToolStripMenuItem

#End Region

Then create the Sub Main method, which will be the entry point for your application.

#Region " Sub Main "

    Public Sub Main(ByVal cmdArgs() As String)
        Application.EnableVisualStyles()

        Dim StartClosed As Boolean = False

        For Each Cmd As String In cmdArgs
            If Cmd.ToLower = "/c" Then
                StartClosed = True
                Exit For
            End If
        Next

        Application.Run(New AppContext(StartClosed))
    End Sub

#End Region

I used the variant that passes command line arguments. In this demo, the application will normally start as a regular window; a command argument of /c tells the application to start closed. The method turns visual styles back on (you will see later why it was disabled; I think this is a VB-only step that C# programmers can ignore), determines how the application will start, then launches the app by calling Application.Run with a new instance of AppContext.

When AppContext is created, the method InitializeApp gets called.

Public Sub InitializeApp()
    'Initialize the menus
    TimerMenu = New ToolStripMenuItem
    mnuShow = New ToolStripMenuItem("Show application")
    mnuSep1 = New ToolStripSeparator()
    mnuExit = New ToolStripMenuItem("Exit application")
    MainMenu = New ContextMenuStrip
    MainMenu.Items.AddRange(New ToolStripItem() _
    {TimerMenu, mnuShow, mnuSep1, mnuExit})

    'Initialize the notification icon
    Notify = New NotifyIcon
    Notify.Icon = My.Resources.MainIcon
    Notify.ContextMenuStrip = MainMenu
    Notify.Text = "Formless tray application"

    'Create the application form
    AppForm = New MainForm
    AppForm.Icon = My.Resources.MainIcon
End Sub

The context menu for the icon gets constructed, the icon itself is instantiated and initialized, and the application form is created and set up.

The rest of MainModule is the code that changes the application state and handles the click events on the icon's context menu.

Public Sub CloseApp()
    Notify.Visible = True
    AppForm.Visible = False
End Sub

Public Sub ExitApp()
    Application.Exit()
End Sub

Public Sub MinimizeApp()
    AppForm.WindowState = FormWindowState.Minimized
End Sub

Public Sub RestoreApp()
    Notify.Visible = False
    AppForm.Visible = True
End Sub

Private Sub mnuExit_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuExit.Click
    ExitApp()
End Sub

Private Sub mnuShow_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles mnuShow.Click
    RestoreApp()
End Sub

TrayMenu.png

When the application is closed, the icon will be in the notification area. Right clicking on it pulls up a menu showing application feedback (in this demo, the current date and time) and options to either show the application form or exit the application completely.

You will see that RestoreApp does not change AppForm.WindowState. If the form is maximized or minimized when it is closed, it will be restored as maximized or minimized.

Note that setting AppForm.Visible = False only hides the form; it does not halt execution of its code. Your application will continue to run unobtrusively, without any clutter in the taskbar. If your app needs to make a distinction between "active" and "background" modes, you can use CloseApp and RestoreApp to switch states.

The Form

The form for the demo is very basic. It has a MenuStrip with options to minimize, close and exit the application, a Timer, a Label named TimeLabel and a TextBox. Clicking the menu items will call the MinimizeApp, CloseApp or ExitApp method from MainModule. In the form's constructor, TimeLabel is set to display the current date and time, and the timer is set to go off every second. When the timer rings, both TimeLabel and TimerMenu are updated to the current date and time.

FormMenu.png
Public Class MainForm

#Region " Constructors "

    Public Sub New()
        InitializeComponent()

        TimeLabel.Text = DateTime.Now.ToString
        TimerMenu.Text = TimeLabel.Text

        Timer1.Interval = 1000
        Timer1.Start()
    End Sub

#End Region

#Region " Event handlers "

    Private Sub MainForm_FormClosing(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.FormClosingEventArgs) _
    Handles Me.FormClosing
        If e.CloseReason = CloseReason.UserClosing Then
            e.Cancel = True
            CloseApp()
        End If
    End Sub

    Private Sub mnuFormClose_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles mnuFormClose.Click
        CloseApp()
    End Sub

    Private Sub mnuFormExit_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles mnuFormExit.Click
        ExitApp()
    End Sub

    Private Sub mnuFormMinimize_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
    Handles mnuFormMinimize.Click
        MinimizeApp()
    End Sub

#End Region

    Private Sub Timer1_Tick(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Timer1.Tick
        TimeLabel.Text = DateTime.Now.ToString
        TimerMenu.Text = TimeLabel.Text
    End Sub

End Class

What makes this all work is the FormClosing event. The FormClosingEventArgs parameter allows us to see why the form is being closed. If it is closing because of user intervention -- that is to say, the user clicked the Close Form button in the upper right corner or selected Close in the app's taskbar context menu -- we can cancel the close and instead call CloseApp.

Project Settings

Because of the way Visual Basic code gets compiled, VB programmers have a bit more work to do before this will work as expected. Go into the Project Settings form (Project in the Visual Studio main menu, then project name Properties at the bottom) and select the Application tab. Uncheck Enable application framework; this tells the compiler that you will be managing the infrastructure. One side effect of this is that visual styles get disabled, which is why we turn it back on manually in Sub Main. In the Startup object drop-down, select Sub Main.

Final Note

I hope you found this article useful. As always, I welcome comments about bugs and other problems in the code, and feedback is always welcome. If some enterprising soul would like to translate this code into C# and post it as an article, please give me credit.

Article history

  • Version 1 - August 25, 2010 - Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here