以下内容便是看完《系统托盘编程完全指南》后写下来的!
这里将比较系统地描述托盘应用的编程。
MFC框架没有提供任何现成的类应用于系统托盘UI,那么如何将表示应用程序的图标添加到任务栏中呢?方法很简单,只用到一个API函数,它就是Shell_NotifyIcon。这个函数本身也相当容易理解和使用。看看它的原型就知道了:
BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA pnid
);
第一个参数dwMessage类型为DWORD,表示要进行的动作,它可以是下面的值之一:
NIM_ADD: 添加一个图标到任务栏。
NIM_MODIFY: 修改状态栏区域的图标。
NIM_DELETE: 删除状态栏区域的图标。
NIM_SETFOCUS: 将焦点返回到任务栏通知区域。当完成用户界面操作时,任务栏图标必须用此消息。
例如,如果任务栏图标正显示上下文菜单,但用户按下"ESCAPE"键取消操作,这时就必须用此消息将焦点返回到任务栏通知区域。
NIM_SETVERSION:指示任务栏按照相应的动态库版本工作。
第二个参数pnid是NOTIFYICONDATA结构的地址,其内容视dwMessage的值而定。这个结构在SHELLAPI.H文件中定义如下:
typedef struct _NOTIFYICONDATA {
DWORD cbSize; // 结构大小(sizeof struct),必须设置
HWND hWnd; // 发送通知消息的窗口句柄
UINT uID; // 图标ID ( 由回调函数的WPARAM 指定)
UINT uFlags;
UINT uCallbackMessage; // 消息被发送到此窗口过程
HICON hIcon; // 图标句柄
CHAR szTip[64]; // 提示文本
} NOTIFYICONDATA;uFlags的值:
#define NIF_MESSAGE 0x1 // 表示uCallbackMessage 有效
#define NIF_ICON 0x2 // 表示hIcon 有效
#define NIF_TIP 0x4 // 表示szTip 有效
有关Shell_NotifyIcon函数的详细使用细节请参考MSDN。
NOTIFYICONDATA结构中的
hWnd 是"拥有" 图标的窗口句柄。
uID可以是任何标示托盘图标的ID(如果有多个图标),一般使用资源ID。
uFlags指明其余的几个成员(hIcon、uCallbackMessage和szTip)的值是否有效。
uCallbackMessage为一个自定义的消息,当用户在该图标上作用一些鼠标动作时,将向hWnd成员中指定的窗口发出该消息,可以自定义该消息。
hIcon可以是任何图标的句柄,包括预定义的系统图标,如IDI_HAND、IDI_QUESTION、IDI_EXCLAMATION、或者Windows的徽标IDI_WINLOGO。
szTip为一字符数组,当鼠标停留在该图标上时,将其内容显示在浮动的提示信息框中。
图标的显示并不难,关键是事件的处理。 当用户将鼠标移到图标上或者在图标上单击鼠标时,为了得到通知消息,你可以将自己的消息ID赋给uCallbackMessage,并设置NIF_MESSAGE标志。当用户在图标上移动或单击鼠标时,Windows将用hWnd指定的窗口句柄调用你建立的窗口过程;消息ID在uCallbackMessage中指定,uID的值即为wParam,lParam为鼠标事件,如WM_LBUTTONDOWN等。
这里先利用API函数,单独在一个文档框架中完成系统托盘功能。
程序启动是将界面隐藏,比较容易实现,只需在CMainFrane类中重载PreCreateWindow()函数并将其按如下代码进行修改即可:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT &cs)
{
...
cs.style = WS_POPUP;
cs.dwExStyle |= WS_EX_TOOLWINDOW;
return TRUE;
}
初始化NOTIFYICONDATA结构,必须在程序一开始执行时就完成,所以要在主框架类的OnCreate()函数中添加代码:
NOTIFYICONDATA tnd;
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = this->m_hWnd;
tnd.uID = IDR_MAINFRAME;
tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnd.uCallbackMessage = WM_TRAY; //WM_TRAY为自定义消息WM_USER+103
tnd.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
strcpy(tnd.szTip, "vic.MINg's System Tray");
Shell_NotifyIcon(NIM_ADD, &tnd);
为了使人机界面更友好,与用户更好的进行交互,使用户在该图标上双击鼠标左键时能弹出程序主界面,再次双击则隐藏。如果用户右键点击图标,则弹出浮动菜单供用户选择使用。可以在自定义消息WM_TRAY的响应函数里加入对鼠标左、右键的处理过程:
//MainFrm.h头文件,自定义消息
//{{AFX_MSG(CMainFrame)
...
afx_msg void OnTray(WPARAM wParam,LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()//MainFrm.cpp源文件,消息处理
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
...
ON_MESSAGE(WM_TRAY,OnTray)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()void CMainFrame::OnTray(WPARAM wParam,LPARAM lParam)
{
// 发出该消息的图标的ID
UINT uID;
// 鼠标动作
UINT uMouseMsg;
POINT pt;
uID = (UINT) wParam;
uMouseMsg = (UINT) lParam;
CMenu menu;
SetForegroundWindow();
// 如果是单击右键
if (uMouseMsg == WM_RBUTTONDOWN)
{
switch(uID)
{
case IDR_MAINFRAME: // 如果是我们的图标
// 取得鼠标位置
GetCursorPos(&pt);
// 执行相应操作
menu.LoadMenu(IDR_BEGINMENU);
pMenu = menu.GetSubMenu(0);
ASSERT(pMenu != 0);
pMenu->TrackPopupMenu(0, pt.x, pt.y, this);
break;
default:
break;
}
}
else if (uMouseMsg == WM_LBUTTONDBLCLK) // 如果是单击左键
{
static bool isMax = false;
switch(uID)
{
case IDR_MAINFRAME:
if (isMax == false)
{
OnMainWindow();
isMax = true;
}
else
{
ShowWindow(SW_HIDE);
ModifyStyle(WS_CAPTION | FWS_PREFIXTITLE | WS_SYSMENU | WS_MINIMIZEBOX, WS_POPUP);
ModifyStyleEx(WS_EX_TOPMOST, WS_EX_TOOLWINDOW);
isMax = false;
}
break;
default:
break;
}
}
return;
}
对鼠标右键弹出的浮动菜单项的处理需要通过自定义消息来响应,这将不能通过ClassWizard向导来完成而要一步步地手动添加。
首先在头文件里添加响应函数的声明:
//{{AFX_MSG(CMainFrame)
...
afx_msg void OnMainWindow();
afx_msg void OnExit();
afx_msg void OnSupport();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
然后,在源文件里添加消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
...
ON_COMMAND(ID_MAIN, OnMainWindow)
ON_COMMAND(ID_EXIT, OnExit)
ON_COMMAND(ID_SUPPORT, OnSupport)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
最后在其各自的实现函数中加入与其菜单项对应的功能代码即可:
void CMainFrame::OnMainWindow()
{
ModifyStyle(WS_POPUP, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
ModifyStyleEx(WS_EX_TOOLWINDOW, WS_EX_TOPMOST);
ShowWindow(SW_SHOW);
}void CMainFrame::OnSupport()
{
AfxMessageBox("vic.MINg");
}void CMainFrame::OnExit()
{
NOTIFYICONDATA tnd;
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = this->m_hWnd;
//保证删除的是本程序的图标
tnd.uID = IDR_MAINFRAME;
Shell_NotifyIcon(NIM_DELETE, &tnd);
AfxPostQuitMessage(0);
}