给MFC程序添加通知区域图标
现在起来越多的程序都有一个通知区域图标(托盘图标),既美观又方便。但是MFC不像C#一样提供NotifyIcon控件,要实现通知区域图标,就要自己做一个CTrayIcon类或者使用NOTIFYICONDATA结构。
MSDN中关于NOTIFYICONDATA的介绍:
http://msdn.microsoft.com/en-us/library/ms910625.aspx
我在这里记录一下自己通过NOTIFYICONDATA实现MFC程序中的通知区域图标的过程,既是分享,也方便以后自己查阅。
1. 在CMainFrame类(或者是CXxxDlg类)中添加一个NOTIFYICONDATA的结构体变量:
NOTIFYICONDATA m_ntIcon;
并在CMainFrame的OnCreate函数中(或者是OnInitDialog)设置该变量:
m_ntIcon.cbSize = sizeof(NOTIFYICONDATA); //该结构体变量的大小
m_ntIcon.hIcon = AfxGetApp()->LoadIconA(IDR_MAINFRAME); //图标,通过资源ID得到
m_ntIcon.hWnd = this->m_hWnd; //接收托盘图标通知消息的窗口句柄
char atip[128] = "a notify icon"; //鼠标设上面时显示的提示
strcpy_s(m_ntIcon.szTip, 128, atip);
m_ntIcon.uCallbackMessage = MY_WM_NOTIFYICON; //应用程序定义的消息ID号
m_ntIcon.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP; //图标的属性:设置成员uCallbackMessage、hIcon、szTip有效
::Shell_NotifyIconA(NIM_ADD, &m_ntIcon); //在系统通知区域增加这个图标
2. 为这个图标添加消息处理
(1)定义回调消息的ID
#define MY_WM_NOTIFYICON (WM_USER+1001)
(2)添加消息映射
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//OnNotifyIcon 就是消息处理函数的名称
ON_MESSAGE(MY_WM_NOTIFYICON, &CMainFrame::OnNotifyIcon)
END_MESSAGE_MAP()
(3)在CMainFrame类(或者是CXxxDlg类)中添加该图标的消息处理函数
.h文件中:
public:
afx_msg LRESULT OnNotifyIcon(WPARAM, LPARAM);
.cpp文件中:
LRESULT CMainFrame::OnNotifyIcon(WPARAM wparam, LPARAM lparam)
{
if(lparam == WM_LBUTTONDOWN)
{
//这里添加对鼠标左键点击的处理,具体处理见(4)
}
else if(lparam == WM_RBUTTONDOWN)
{
//这里添加对鼠标右键点击的处理,具体处理见(5)
}
return 0;
}
(4)对鼠标左键点击的处理:
我一般的处理是如果窗口是正常显示的,点一下将窗口最小化到系统托盘;如果窗口是最小化的,点一下则将其恢复到正常显示。
这里我通过一个bool成员变量m_bMin来判断当前窗口的状态
//恢复窗口或者最小化
if(m_bMin == true)
{
AfxGetMainWnd()->ShowWindow(SW_SHOW);
AfxGetMainWnd()->ShowWindow(SW_RESTORE);
//这里貌似只有写这样两句才能保证恢复窗口后,该窗口处于活动状态(在最前面)
m_bMin = false;
}
else
{
AfxGetMainWnd()->ShowWindow(SW_MINIMIZE);
m_bMin = true;
}
其中m_bMin的值,可以在OnSize(UINT nType, int cx, int cy)中进行设定,如果nType为SIZE_MINIMIZED时,将其设为true;其它则设为fasle。具体代码就不在这里写了。
(5)对鼠标右键点击的处理:
我一般是鼠标右键点击,则弹出一个右键菜单:
//弹出左键菜单
CMenu popMenu;
popMenu.LoadMenuA(IDR_MENU_POPUP); //IDR_MENU_POPUP是在ResourceView中创建并编辑的一个菜单
CMenu* pmenu = popMenu.GetSubMenu(0); //弹出的菜单实际上是IDR_MENU_POPUP菜单的某项的子菜单,这里是第一项
CPoint pos;
GetCursorPos(&pos); //弹出菜单的位置,这里就是鼠标的当前位置
//显示该菜单,第一个参数的两个值分别表示在鼠标的右边显示、响应鼠标右击
pmenu->TrackPopupMenu(TPM_RIGHTALIGN|TPM_RIGHTBUTTON, pos.x, pos.y, AfxGetMainWnd(), 0);
对于这个菜单的项及其处理,写在后面。
3. 程序退出时,删除该图标
这一步一定不能没有,不然的话,程序退出后,通知区域还有这个图标,只有鼠标放上去后才会消失。
在OnDestroy()函数里面:
//删除该图标
::Shell_NotifyIcon(NIM_DELETE, &m_ntIcon);
4. 关于弹出的右键菜单
我做的简单的右键菜单包含”显示窗口“,”关于...“和”退出“,可以在ResuourceView中,对该菜单添加消息处理函数。
(1)显示窗口
点这个菜单,这显示程序的主窗口,消息处理函数如下:
void CMainFrame::OnPopupShow()
{
// TODO: Add your command handler code here
AfxGetMainWnd()->ShowWindow(SW_SHOWNORMAL );
}
(2)关于...
弹出一个”关于“对话框:
void CMainFrame::OnPopupAbout()
{
// TODO: Add your command handler code here
CAboutDlg abdlg;
abdlg.DoModal();
}
(3)退出
发送一个WM_CLOSE消息,关闭窗口
void CMainFrame::OnPopupQuit()
{
// TODO: Add your command handler code here
SendMessage(WM_CLOSE);
}
这里要注意的是:如果主窗口的关闭按钮点击后,不是退出程序,而是最小化到系统托盘(现在越来越多的程序是这样了,可以在OnClose()函数中实现),就不能通过
SendMessage(WM_CLOSE);
来关闭窗口了。我用的方法是
this->DestroyWindow();
但不确定这个方法是否合适。