Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Create Icons at Runtime and Show Them in the System Tray

4.66/5 (18 votes)
10 Jun 2008CPOL4 min read 1   2.2K  
Create dynamic icons at runtime and show them in the system tray

Introduction

I use a tool to observe two measured values from different thermometers via RS232. When I work with other applications on my computer during the observation time, I can't see the measured values in the dialog window, because the tool is overlaid with the window of the application. So I needed a display which is independent from the state of the desktop. I decided to write the values in icons and show these icons in the system tray repetitively.

DynIcon

After reading other articles about icons in the system tray, you might ask what is special in this demo? The main point of interest in this demo is the dynamic creation of the icons at runtime in a simple way and updating of the system tray permanently with these icons.

Two Versions

There are two versions of the code. The first time, I submitted the article with the MFC demo project and explained the code. Six months later, I updated the article with a C# version. Because the C# code is much easier to understand, I leave the explanation for the MFC code untouched and just added the C# demo project. In this new project, you will find nearly all the methods and variables as in the MFC code because the principle has not changed.

Using the Code

The tool works like this: When the program starts, a static (default) icon appears in the system tray. Just when you push the 'Start' button, there will be two icons which show the changing values. You can hide the dialog with the minimize button. The program will disappear from the task bar, and only the changing icons will be shown in the system tray. With a double click on the icons, the dialog will popup to the desktop (or will be hidden from the desktop if the dialog was visible).

In the demo project, I use a counter to simulate the dynamics of the measured values. It is realized by a timer. At each timer event TIMER1, the values are changed, the icons are drawn and then pushed into the system tray:

C++
//CDynIconDlg::OnTimer(UINT_PTR nIDEvent)
switch(nIDEvent){
    case TIMER1:
        ChangeValues();
        CreateIcons();
        PushIcons();
        break;

    default: break;
}

How to Create Icons?

In this demo, I show how to load and display a static icon from the resource view of the IDE and how to draw an icon at runtime. I use a static icon for the default icon, which was added and designed in the resource view. I use only the 16x16 pixel image type. The 32x32 pixel image type could be deleted or you use the same icon for the IDR_MAINFRAME icon. The resource name is IDI_ICON1. This name is necessary for the NOTIFYCONDATA structures which will push the icon into the system tray (see below). To load the default icon at runtime, I use the following code:

C++
//CDynIconDlg::SetDefaultIconInTray()
//create a handle for the default icon
HICON hIcon;

//load the icon from the resources
HINSTANCE hInst = 
    AfxFindResourceHandle(MAKEINTRESOURCE(IDI_ICON1),RT_GROUP_ICON);

hIcon = (HICON)LoadImage(hInst,MAKEINTRESOURCE(IDI_ICON1),
        IMAGE_ICON,16,16,LR_DEFAULTCOLOR);

//push the icon into the system tray
    .
    .
    .

Creating Icons at Runtime

Because a GDI+ graphics object is used, I implemented GDI+. How to do it is written in the article, Starting with GDI+.

This happens in CreateIcon1() and CreateIcon2().
At first we have to prepare the value which should be written into the icon. For the used command DrawString(), we need a widechar array. Either we write the value directly to the widechar array strValue...

C++
//CDynIconDlg::CreateIcon1()
//we need a widechar array for the value
wchar_t strValue[4];

// because the wchar_t requires two bytes
int size = sizeof(strValue)/sizeof(wchar_t);

//the prefix L means, that the string should be a widechar
swprintf( strValue, size, L"%3d", m_nValue1 );

... or we write the value in a char array and convert it to a widechar array.

C++
//CDynIconDlg::CreateIcon1()
char txt[4];
wchar_t strValue[4];
// because the wchar_t requires two bytes
int size = sizeof(strValue)/sizeof(wchar_t);

//write the value1 in the string
sprintf_s(txt, sizeof(txt),"%3d", m_nValue1);

//change to widechar
MultiByteToWideChar(CP_ACP, 0, txt, -1, strValue, size);

In the next step, we provide a Pen, a SolidBrush, a Font and some formatting stuff:

C++
//CDynIconDlg::CreateIcon1()
//you can change the colors depending on the value
//here it should be white only
//white pen to draw some lines in the icon
Pen whitePen(Color(255, 255, 255));

//white brush for the text
SolidBrush whiteBrush(Color(255,255,255));

//create a small font to write the values in the bitmap
Font font(L"Tahoma",8);

//origin for the string
PointF origin(-1, 1);

//alignment for the string
StringFormat format;
format.SetAlignment(StringAlignmentNear);

Now let's start with painting:

C++
//CDynIconDlg::CreateIcon1()
//create a new bitmap with 16x16 pixel
Bitmap bitmap(16,16);

//use the bitmap to draw
Graphics *graph = Graphics::FromImage(&bitmap);

//draw two horizontal lines (just for the show)
graph->DrawLine(&whitePen, 0, 15, 15, 15);
graph->DrawLine(&whitePen, 0,  0, 15,  0);

//draw the string including the value
//here we need the widechar array
graph->DrawString(strValue , -1, &font, origin, &format, &whiteBrush);

And at last, we convert the bitmap into an icon and save it to our icon handle m_hIcon1. This handle is a member of our class because we need it later in PushIcon1() to push it into the system tray.

C++
//CDynIconDlg::CreateIcon1()
bitmap.GetHICON(&m_hIcon1);

Push the Icon into the System Tray

First we need a NOTIFYCONDATA structure filled with some data including the name ICON_VALUE1, a user defined message WM_TRAY for the double clicks on the icon and the handle m_hIcon1 of the created icon.

C++
//CDynIconDlg::PushIcon1()
// set NOTIFYCONDATA structure
NOTIFYICONDATA tnid;

tnid.cbSize = sizeof(NOTIFYICONDATA);
tnid.hWnd = m_hWnd;
tnid.uID = ICON_VALUE1; //Resourcename

tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnid.uCallbackMessage = WM_TRAY;//user message

tnid.hIcon = m_hIcon1; //handle of the created icon

Take care of the resource names. There are three different names. The static default icon has the name IDI_ICON1 and the other two icons have the self defined names ICON_VALUE1 and ICON_VALUE2 (see DynIconDlg.h).

It is possible to use a tooltip. After every change of values, the string m_strTooltip will be updated. In this demo, I use the same tooltip for both icons and use the string as a member variable.

C++
//CDynIconDlg::PushIcon1()
//copy the string to the NOTIFYCONDATA structure
lstrcpyn(tnid.szTip, m_strTooltip, sizeof(tnid.szTip));

Now we have to check if the icon appears for the first time. Then we have to use the Shell_NotifyIcon with the NIM_ADD parameter, otherwise we just want to update the icon with the NIM_MODIFY parameter.

C++
//CDynIconDlg::PushIcon1()
if(m_bFirstIcon1){

    //for the first time we have to use the Shell_NotifyIcon with NIM_ADD
    //to add the icon to the tray
    Shell_NotifyIcon(NIM_ADD, &tnid);
    m_bFirstIcon1 = FALSE;
}
else{
    //the icon already exists
    //Shell_NotifyIcon with NIM_MODIFY
    Shell_NotifyIcon(NIM_MODIFY, &tnid);
}

Now the icon is in the system tray and we clean up:

C++
//CDynIconDlg::PushIcon1()
//frees the memory
DestroyIcon(m_hIcon1);

The same procedure is also followed for the second icon in CreateIcon2() and PushIcon2().

Comments

The other methods in the code are commented. So I made my explanations short. I know that the code is not written very efficiently. But I think this enforces the understanding of the used methods.

History

  • 29th November, 2007 - Created the article
  • 10th June, 2008 - Updated the article with C# code

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)