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

Tray Icon Class with Icon Animation Abilities

0.00/5 (No votes)
14 Dec 2003 1  
A class which makes tray icons management and animation really easy

Introduction

When I started working on my current project, I noticed that lots of programs I was working on, required notification icons (yeah, tray icons) because of some reasons. Actually, writing a couple of calls to Shell_NotifyIcon() is not a big deal, but this project required animated tray icon. And I decided that I have something to do with all these tray icons. The result of my work is here. Meet � CakTrayIcon and CakTrayIconAnimator classes!

Background

If you want to create a similar class, you'll need to read about the famous Shell_NotifyIcon() function and take a look at the IconPro MSDN sample (see "Icons in Win32" article by John Hornick from Sep 29, 1995).

Shell_NotifyIcon() will give you an ability to control notification icon and the sample will show you how to extract planes from the icon resource.

If you do not know yet, the icons in Windows can have multiple so-called planes. The plane is actually an image. Usually icons have 16x16x16, 32x32x256 and 48x48x256 planes, so Windows Explorer can display the icons correctly in different video and list modes. But the icons can have saying, 10 16x16x16 planes or 25 32x32x16M planes � whatever you want. I decided to use such kind of the icons to make animated notification icons.

Don't worry � almost all good icon editors can make such icons. Not the editor of Visual Studio, though.

Using the Code

Using CakTrayIcon is easy. Just declare the object of this class.

void CMainFrm::SomeImportantFunc()
{
    CakTrayIcon ti(this, 1,                 //  1 is a tray icon identifier

        LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME)), 
        _T("Some tip"));
    //

    //  Do something

    //

    return;             //  The icon will disappear automatically

}

Your icon should be visible all the time? No problem.

First, edit your header file.

class CMainFrm : public CFrameWnd
{

private:
    CakTrayIcon m_TrayIcon;

};

Then, initialize the icon in OnCreate handler.

int CMainFrm::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    m_TrayIcon.Create(this, 1);  //  1 is a tray icon identifier

    m_TrayIcon.SetIconAndTip(LoadIcon(AfxGetInstanceHandle(), 
     MAKEINTRESOURCE(IDR_MAINFRAME)), 
        _T("This is a tray icon"));

    return 0;
}

That's all. The icon will be destroyed when the window will be closed.

Handling Events

No problem to handle the events from your icon too.

Declare the message handler in the header.

class CMainFrm : public CFrameWnd
{

private:
    afx_msg LRESULT OnTrayIconNotify(WPARAM, LPARAM);

};

And add it to the message map.

BEGIN_MESSAGE_MAP(CMainFrm, CFrameWnd)
    ON_REGISTERED_MESSAGE(CakTrayIcon::WM_TRAYICONNOTIFY, OnTrayIconNotify)
END_MESSAGE_MAP()

LRESULT CMainFrm::OnTrayIconNotify(WPARAM, LPARAM)
{
    AfxMessageBox("Yabba-dabba-doo!");
    return 0;
}

Please, refer to NOTIFYICONDATA.uCallbackMessage for details about the message you'll receive. Note that you'll receive the id of the icon in wParam parameter. Also note that you should use ON_REGISTERED_MESSAGE macro along with CakTrayIcon::WM_TRAYICONNOTIFY parameter to add the notification message into the map.

More Stuff

Implementing "just another tray icon class" is boring. So I added a couple of helper functions.

You can change an icon, a tip or both using the functions below.

//  Change only icon

m_TrayIcon.SetIcon(LoadIcon(0, IDI_ERROR));

//  Change only tip

m_TrayIcon.SetTip(_T("Hi there!"));

//  Change both icon and tip

m_TrayIcon.SetIconAndTip(LoadIcon(0, IDI_ERROR), _T("Hi!"));

And, of course, you can show an information balloon.

//  Popup the balloon

m_TrayIcon.PopupBalloon(_T("Some information"), _T("Balloon title"), 
    NIIF_INFO, 10000);

Note that balloon tip can be used only if you targeted IE version 5.0 or later (see _WIN32_IE macro). In other cases you will be able to call this method, but nothing will happen.

Wake Up! The Most Interesting Part!

Before using the following code, you'll need at least one multi-plane icon. Please, take a look at hourglass.ico in folder res of the sample project for example.

Assume you want to start lenghtly operation. You'd like to animate something in the system notification area to make the process more interesting for user. Use CakTrayIconAnimator for this reason.

And again, this is simple as 1-2-3.

void SuperDuperFunction()
{
    CakTrayIconAnimator tia(&m_TrayIcon, IDI_HOURGLASS, _T(
     "Hey! Don't get sleep!"));
    tia.Animate(500);  //  Each plane will be visible for 500 ms 

                       // (300 ms by default)

    AfxMessageBox(
   _T("Click OK when you tired to look at the hourglass spinning around..."),
    MB_OK);
    //  Animation will stop when object of CakTrayIconAnimator 

    //  will be destroyed

}

Note that you have to have a valid object of CakTrayIcon class and an integer code or string name of the multi-plane icon resource. CakTrayIconAnimator will save the current tray icon and tip and restore them after animation completed.

That's it. You don't have to write tons of code to show and animate your icons. Animation is implemented using thread, so you don't have to write any support code for it.

Points of Interest

When I was working on CakTrayIconAnimator class, I found that extract a plane from the icon is a pretty tricky thing. Thanks God, I found IconPro sample by John Hornick in MSDN and added several functions from there to CakIconLoader class. I made this class public, so you can use it if you need to extract icon planes too.

void DoSomething()
{
    CakIconLoader ldr(IDI_HOURGLASS);
    UINT count = ldr.GetIconsCount();   //  Get number of planes

    HICON hIcon = ldr.GetIcon(0);       //  Extract the first plane

}

Don't call DestroyIcon() after GetIcon() � this icon handle will be freed by next GetIcon() call or when object of CakIconLoader will be destroyed.

Interesting fact � when working on CakIconLoader, I found that I should use CreateIconFromResourceEx() with zeroed flags instead of CreateIconFromResource() function. CreateIconFromResource() creates shared icon and this mean that you'll have resource leak when call it constantly. This difference cost me lots of nerves and time.

Known Issues

Burn in your mind that all Windows except XP, 2003 and Me can display tray icons only in 16 colors (despite what MSDN says). So, if you want your icons to look good, make them 16x16x16 if you target any Windows except XP, 2003 and Me, and make hi-color version for XP, 2003 and Me.

History

  • Version 1.0 so far.

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