Home |
Tray Notify Icon Put an Icon in the Task Bar Status Area |
Home |
Many programs will display a small Icon on the right side of a horizontal Taskbar (the status area with the clock), which is called a Tray icon. These tray icons are intended to be used as a Status Indicator for non-visible programs or lengthty processes like printing. The user can move his mouse over the small Icon to get a Tip, or the user can right click and get a pop-up menu, or left click to show a window or dialog box. The Shell_NotifyIcon( ) function is used to place a tray icon in the task bar status area. Once the tray icon is shown, your program will get notification messages about mouse activity in the area of the tray icon. You can use these messages to show menus, show windows, stop a process, or exit your program. You must always delete any Tray Icon that you have created, because the system does not automaticly delete it. |
Old and New Tray Icon Versions In the windows 2000 OS the tray icon was given more functionality, it added the "Ballon" tray icon Tip, so the system NOTIFYICONDATA structure in API was give more members for this "Ballon" tip. I will Not have anything in this lesson about the newer tray icon with ballon tips. The code here will be for the API of the windows 95 version, so all of the code here will work in all 32 bit versions of windows. The Shell_NotifyIcon( ) function function Shell_NotifyIcon( dwMessage: Cardinal; // message identifier lpData: PNotifyIconData // pointer to TNotifyIconData ): Boolean;There are three dwMessage flags, NIM_ADD, to add an Icon to the task bar, NIM_MODIFY, to change the Icon, and NIM_DELETE, to delete an Icon from the task bar. You can only use ONE of these messages in a call to Shell_NotifyIcon. The next parameter, PNotifyIconData is defined in the shellapi.pas as - PNotifyIconDataA = ^TNotifyIconDataA; PNotifyIconData = PNotifyIconDataA; _NOTIFYICONDATAA = record cbSize: Cardinal; Wnd: HWND; uID: Cardinal; uFlags: Cardinal; uCallbackMessage: Cardinal; hIcon: HICON; szTip: array [0..63] of AnsiChar; end; TNotifyIconDataA = _NOTIFYICONDATAA; TNotifyIconData = TNotifyIconDataA; The members of the TNotifyIconData record cbSize - Size of the TNotifyIconData record used, set to SizeOf(TNotifyIconData) for old style tray icon. hWnd - Handle of the window that receives mouse event notification messages from a tray icon in the taskbar status area. uID - the ID number of the taskbar icon, used if there is more than one tray icon for your program. uFlags - Flag bits that indicate which of the other record members contain data that will be used by the Shell_NotifyIcon function. The uFlags can be a combination of these values:
uCallbackMessage - Your windows message number, which must be in the range of WM_USER messages. The system uses this message for notification that it sends to the window identified by hWnd whenever a mouse event occurs in the bounding rectangle of the tray icon. hIcon - Handle of the icon used in the tray. szTip - Tooltip text to display for the icon, which can contain up to 63 characters.
An example of code used to place a Tray Icon in the Task bar - var IconData: TNotifyIconData; begin IconData.cbSize := SizeOf(TNotifyIconData); IconData.Wnd := hForm1; IconData.uID := 12; IconData.uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; IconData.uCallbackMessage := WM_USER + 144; IconData.hIcon := hMyIcon; IconData.szTip := 'a Tray Icon Tip'; Shell_NotifyIcon(NIM_ADD, @IconData); end;IMPORTANT, You must DELETE the Tray Icon Once a Tray Icon is placed in the Task bar, it is not associated with your program except that it sends notification messages to it. So if you exit your program the Tray Icon will still be in the task bar, it is NOT removed or deleted with your program's closing. (although in newer systems there seems to be some checking for a windows existance). ALWAYS have a call to delete the tray icon that you have created, like this - Shell_NotifyIcon(NIM_DELETE, @IconData);The IconData must have two members set, the IconData.Wnd and the IconData.uID as the values you created the tray icon with. The uCallbackMessage Notification The window whose handle is placed in the IconData.Wnd above, will recieve the mouse events as a message with the value given in the IconData.uCallbackMessage. You must use a message number in the WM_USER range, which is WM_USER up to WM_USER + 32767. This message will go to the Window Proc of that window. See the WM_TRAYICONCLICKED message of the function MessageFunc( ) in the Tray Icon Program code below. All of the mouse (client area click) messages are sent, these include - WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_RBUTTONUP, WM_MBUTTONUP, WM_MBUTTONDOWN and the other right and middle button click messages. The WM_MOUSEMOVE is also sent. The WM_MOUSEWHEEL message is NOT processed and passed to your program, so you will not get any mouse wheel activity tray icon messages. Pop Up Menu Creation In the TrayIcon program code below, I have the WM_TRAYICONCLICKED message for the WM_RBUTTONUP event show a Pop-Up menu, as most tray icons do. I have the ShowPopMenu( ) procedure for the code that makes this pop-up menu. You can see more about using API menus here at DelphiZeus in Lesson 10 - Menus and List Boxes. There is one addition to the code you find there for pop-up menus used here. Since the system tray with your icon in it does not get focus when you click it, your pop-up menu may not close if you click on something else. If you add the code - SetForegroundWindow(hForm1); so something has focus, it seems to solve this problem. A Note About Tray Icons Please try and think about how your tray icon will Help (improve or quicken operation) for your user. You should have your Tray Icon as a "Status" indicator for your program, this means you should change the Icon, or at least the "Tip" as your program does things. Also, I beleive it is a misteak to think that your user will want your tray icon. Tray Icons are now widely used by many popular programs, (volume-control, printer, eMail, anti-virus, messenger, music, system). I have seen people's computers with more than 20 Tray Icons, so many tray icons that the user did NOT know what most of the icons were for, and NEVER-EVER used some of these icons (did not ever click them or look at them). The user wanted to "Get Rid" (delete or not show) many of the tray icons they did not know and did not use, but many programs did not offer that option (no tray icon) or you had to open up the it's main program and search through several "Operation" windows and find a window with a menu that had an "Options" for tray Icons, and try to find the no-show tray icon option. If possible have your "Tray Icon Options" in your tray icon pop-up menu. If possible ask your user if they want your icon in their tray, before you place it there, or at least offer them a way to "Not Show" your icon in their tray. |
This program will create an invisible (hidden) Main Form window using the WS_EX_TOOLWINDOW in the CreateWindowEX function. Even if you show this window it will not be seen because the width and height are set to Zero. If you include the WS_EX_TOOLWINDOW flag in the EX style then the window will not place a button in the Task bar when it is shown. A tray icon is created with Shell_NotifyIcon(NIM_ADD, @IconData);, if you look at the procedure Do101; and procedure Do102; you will see code to Modify the tray icon. A Pop-Up Menu is created and shown in the procedure ShowPopMenu( ) which is called for a right click on the tray icon. Since the ShellApi is in the uses clause, I have the ShellExecute( ) function in this program, which you can see in procedure Do102; and procedure Do103;. |
program TrayIcon; {this Program creates an Invisible window, and places a Tray Icon on the Task bar. the Tray icon can be right clicked for a menu} uses Windows, Messages, ShellApi; {you need to include ShellApi in the uses for the Shell_NotifyIcon} {$R *.RES} var wClass: TWndClass; hForm1: Integer; MainMsg: TMSG; IconData: TNotifyIconData; const Zero = 0; WM_TRAYICONCLICKED = WM_USER + 1776; {the tray icon clicked message has to be a WN_USER message} procedure ShutDown; begin {be sure to Delete all the Tray Icons you create} Shell_NotifyIcon(NIM_DELETE, @IconData); PostQuitMessage(Zero); end; procedure ShowPopMenu(ShowPt: TPoint); {this procedure creates and shows, and destroys an Popup menu} var hPopMenu: HMENU; begin hPopMenu := CreatePopupMenu; {this CreatePopupMenu fuction makes an empty menu} SetForegroundWindow(hForm1); // Add this SetForegroundWindow to have this Pop-Up Menu // close if you click something that is NOT this menu {AppendMenu() adds Items to a menu} AppendMenu(hPopMenu, MF_STRING, 101,'Show message'); {the 101 is the ID number sent to the WndProc message function when menu click} AppendMenu(hPopMenu, MF_STRING, 102, 'Open Notepad'); AppendMenu(hPopMenu, MF_STRING, 103, 'Open Yahoo web page'); AppendMenu(hPopMenu, MF_SEPARATOR, 0,nil); AppendMenu(hPopMenu, MF_STRING, 104, 'Exit Tray Icon'); {the TrackPopupMenu fuction is active as long as the PopUp Menu is displayed, so nothing else is happening in this thread while the menu is up} TrackPopupMenu(hPopMenu, // handle of shortcut menu TPM_LEFTALIGN or TPM_LEFTBUTTON, // screen-position and mouse-button flags ShowPt.x-5, // horizontal position, in screen coordinates ShowPt.y-5, // vertical position, in screen coordinates Zero, // reserved, must be zero hForm1, // handle of window that gets menu messages { see WndProc at the WM_COMMAND for menu messages} nil // points to RECT that specifies no-dismissal area ); DestroyMenu(hPopMenu); end; procedure Do101; begin MessageBox(0,'the Do101 Message box, the Tray Icon Tip will be changed', 'just a message box',MB_OK or MB_ICONINFORMATION); IconData.uFlags := NIF_TIP; IconData.szTip := 'New Tip, Click will get a Menu'; Shell_NotifyIcon(NIM_MODIFY, @IconData); {the NIM_MODIFY flag tells the system to change the Tray Icon according to the flags in the IconData.uFlags. Here the NIF_TIP flag is set and a new szTip is given, so a new Tip will be seen} end; procedure Do102; begin IconData.uFlags := NIF_ICON; IconData.hIcon := LoadIcon(0, IDI_HAND{IDI_QUESTION}); Shell_NotifyIcon(NIM_MODIFY, @IconData); {NIM_MODIFY with the NIF_ICON flag will change the Tray Icon to the System Error Icon given in the IconData.hIcon} ShellExecute(hForm1, 'open', 'notepad.exe', nil, nil, SW_SHOWNORMAL); end; procedure Do103; begin ShellExecute(hForm1, 'open', 'http://www.yahoo.com', nil, nil, SW_SHOWNORMAL); end; function MessageFunc(hWnd, Msg, wParam, lParam:Integer):Integer; stdcall; {this is the fuction that gets the window messages and test's the message to see if code should be executed} var Pt1: TPoint; begin case Msg of {menu click messages are lParam = Zero} WM_COMMAND: if lParam = Zero then begin case LOWORD(wParam) of {the menu ID number is in the LOWORD position of wParam} 101: Do101; 102: Do102; 103: Do103; 104: PostMessage(hForm1,WM_CLOSE,Zero,Zero); {to end this program you post the WM_CLOSE message} end; end; WM_TRAYICONCLICKED: begin {this is a user message used for a Tray Icon Mouse Message, you will need to test for the mouse messages, The wParam has the ID number of the Tray icon, which is used to separate the WM_TRAYICONCLICKED messages if you have 2 or more Tray Icons for your program} if (lParam = WM_RBUTTONUP) then begin {this shows a Menu on a Tray Icon Right Click} GetCursorPos(Pt1); {GetCursorPos seemed to be an easy way to get where the mouse was clicked} ShowPopMenu(Pt1); end else if lParam = WM_LBUTTONUP then {this shows an Abot box on a Left Click} ShellAbout(0, 'About the Tray Icon#This is Version 1-1-1', 'This is About the Tray Icon, it show''s how to use the Shell_NotifyIcon', wClass.hIcon) else if lParam = WM_LBUTTONDBLCLK then PostMessage(hForm1,WM_CLOSE,Zero,Zero); {This Exits the program on a Double Click} end; WM_DESTROY: ShutDown; end; // case Result:=DefWindowProc(hWnd,Msg,wParam,lParam); end; begin // MAIN BEGIN / / / / / / / / / / / / / / / / / / / / / / with wClass do begin Style:= Zero; lpfnWndProc:= @MessageFunc; hInstance:= SysInit.HInstance; hbrBackground:= COLOR_BTNFACE+1; hIcon:= LoadIcon(hInstance,'MAINICON'); hCursor:= LoadCursor(Zero,IDC_ARROW); lpszClassName:= 'First Class'; end; windows.RegisterClass(wClass); hForm1 := CreateWindowEx(WS_EX_TOOLWINDOW, wClass.lpszClassName, 'NoSee', WS_POPUP, Zero, Zero, Zero, Zero, Zero, Zero, hInstance, nil); {this hForm1 is created as a tool window and there is no WS_VISIBLE style bit, so the window is hidden, even if you show the window, it will not be seen because the height and width are Zero and it will not have a button on the tack bar because it is a tool window} {this IconData is used by the Shell_NotifyIcon to get that Tray Icon} with IconData do begin cbSize := SizeOf(TNotifyIconData); {as with most windows records set the size to the record size} Wnd := hForm1; {Wnd is the window that will get the notify message} uID := 5; {I don't use the ID but I set it to 5, the ID is used if you create more than one Tray icon} uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; {these flags indicate what will be set when the Shell_NotifyIcon is called, if you do not have the NIF_TIP, then no tip will be set} uCallbackMessage := WM_TRAYICONCLICKED; {set the callback message here, to be used in the MessageFunc} hIcon := wClass.hIcon; {wClass.hIcon is the MAINICON} szTip := 'Tray Icon, Right Click to get a Menu'; end; Shell_NotifyIcon(NIM_ADD, @IconData); {Shell_NotifyIcon with the NIM_ADD parameter will add a tray icon} while GetMessage(MainMsg,Zero,Zero,Zero) do begin TranslateMessage(MainMsg); DispatchMessage(MainMsg); end; end. |
Next
It's time to make a Text Editing program like Notepad, called TestText, it reads and writes to the registry.
15. TestText Text Editor