Animating System Tray Icons

 

This article explains how to animate an icon in the Shell tray
and assign dynamically changing tooltips for each icon phase. So
the end result will be that your icon can now visually indicate
what your application is currently doing along with corresponding
tooltip descriptions. User notifications like left click and
right click on the icon are also handled. The Windows NT 
resource usage indicator on the Shell Tray is a wonderful example
of animating icons on the Shell Tray. Let’s animate now !.

Step 1: (Setting up the data for the animation)

The first step involves setting up well organized arrays that
kind of keep track of the icons lined up for animation along with
their tooltip description strings. Also define a user defined
notification that will let you know if the user clicked on the
animating icon. Swing by at your resource editor and create all
the icons that you need and give them the same ids specified in
the array.

#define NUM_ICONS_IN_ANIMATION 5

static int iconResourceArray[NUM_ICONS_IN_ANIMATION] =
{ IDI_ICON1, IDI_ICON2, IDI_ICON3, IDI_ICON4, IDI_ICON5 };


static CString strToolTipArray[NUM_ICONS_IN_ANIMATION] =
{
 CString("Resource Usage = 20%") ,
 CString("Resource Usage = 40%") ,
 CString("Resource Usage = 60%") ,
 CString("Resource Usage = 80%"),
 CString("Resource Usage = 100%") };


#define MYMSG_NOTIFYICON (WM_USER + 100)

Step 2:(Adding an useful helper function)

Take a look at this useful helper function below that wraps up
the Shell_NotifyIcon call. The first  parameter indicates to
the helper whether you need to add,delete, or modify an icon in
the Shell Tray by specifying NIM_ADD, NIM_DELETE, or NIM_MODIFY.
The second parameter indicates the index of the icon in the array
that you set up in Step 1. The third parameter indicates the
tooltip string for the currently displayed icon.

void CShellAnimDlg::InstallAnimatedIcon(DWORD dwMsgType,UINT nIndexOfIcon, CString strToolTip)
{

	//Load the specified icon from the array
	HICON hIconAtIndex = AfxGetApp()->LoadIcon(iconResourceArray[nIndexOfIcon]);


	///Fill up the NOTIFYICONDATA Structure
	NOTIFYICONDATA iconData;
	iconData.cbSize		= sizeof(NOTIFYICONDATA);
	iconData.hWnd		= GetSafeHwnd();  //window's handle
	iconData.uID			= 100 ;  //identifier
	iconData.uFlags		= NIF_MESSAGE|NIF_ICON|NIF_TIP; //flags
	iconData.uCallbackMessage	= MYMSG_NOTIFYICON; //notification handler
	iconData.hIcon		= hIconAtIndex;    //icon handle

	//Fill up tool tip
	LPCTSTR lpszToolTip = strToolTip.GetBuffer(strToolTip.GetLength());
	lstrcpyn(iconData.szTip,lpszToolTip,strlen(lpszToolTip)+1);

	//Tell the shell what we intend doing
	//Add,Delete,Modify ---> NIM_ADD,NIM_DELETE, NIM_MODIFY in dwMsgType
	Shell_NotifyIcon(dwMsgType, &iconData);


	//Resources are precious !
	if (hIconAtIndex)
		DestroyIcon(hIconAtIndex);
}

 

Step 3: (Animating the icon)

Whenever you need to start the animation, begin by adding an
icon first on the tray by calling the helper function with
NIM_ADD. Subsequently, cycle through the animation by calling
NIM_MODIFY. The following code which is a dialog based
application adds the first icon in OnInitDialog() and then sets
up a timer that cycles through the animation.

BOOL CShellAnimDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    ///usual initialization goes here
    int nIndexFirstIcon = 0;
    InstallAnimatedIcon(NIM_ADD,nIndexFirstIcon,strToolTipArray[nIndexFirstIcon]);
    SetTimer(1,1000,NULL);
	
    return TRUE; // return TRUE unless you set the focus to a control
}
void CShellAnimDlg::OnTimer(UINT nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	static int nCounter = 0;
	InstallAnimatedIcon(NIM_MODIFY,nCounter,strToolTipArray[nCounter]);
	m_nCounter = nCounter; //Store it here to keep track of the last displayed icon
	nCounter++;
	nCounter = nCounter%(NUM_ICONS_IN_ANIMATION);

	CDialog::OnTimer(nIDEvent);
}

Please Note: You may need to get the last icon off the tray
when the application closes. So make sure you get the icon off
the tray when your application closes by calling:

KillTimer(1);
InstallAnimatedIcon(NIM_DELETE,m_nCounter,"");

 

Step 4: (Handling notifications)

We now  need to keep track of  user actions on our
icon.. Remember the user defined message id MYMSG_NOTIFYICON that
we passed for callback notification to the NOTIFYICONDATA
structure in the helper function. Well, the icon posts a message
whenever the user performs an action on the icon. We are
interested only on mouse clicks and so let’s handle them.
Firstly, go ahead and define a user defined message as shown in
step 1. Once you’re done with that , map the message handler to a
function as shown below.

afx_msg void OnHandleIconNotify(UINT
wParam, LONG lParam);
              
//function to handle notifications

ON_MESSAGE(MYMSG_NOTIFYICON,OnHandleIconNotify)
     //message mapped to function

void CShellAnimDlg::OnHandleIconNotify(UINT wParam, LONG lParam)
{
    CString strTest;
    CString strCurrPosition(strToolTipArray[m_nCounter]);
    switch (lParam)
        {
        case WM_LBUTTONDOWN:
            break;
        case WM_RBUTTONDOWN:
            break;
        case WM_LBUTTONDBLCLK:
            ///Do whatever you need to do when the click occured
            //Current icon animation position is at m_nCounter

        default:
            break;
        }

}

 

You now have your animated icon cycling through it’s phases on
the Shell Tray with different tool tips depending on the icon
position in the cycle order.

Last updated: 17 June 1998

More by Author

Must Read