写在最前面的
将MFC中托盘功能模块抽象成一个类,把整个程序的功能模块细分了。这个想法在网上已经是俯拾即是了,但仍旧不能一下子明白其中的东西,特别是将其抽象之后。 在使用这个类的时候,需要注意:托盘菜单的ID要和图标资源的ID一样,否则会出错。
具体实现代码
添加新的类,选择父类是CCmdTarget,下面的代码中有足够的提醒:
TrayIcon.h
#pragma once
// TrayIcon.h : 头文件
//
// 继承自CCmdTarget才能接收消息,详见《深入浅出 MFC》
class CTrayIcon : public CCmdTarget
{
DECLARE_DYNAMIC(CTrayIcon)
public:
CTrayIcon(UINT uID);
~CTrayIcon();
// Call this to receive tray notifications
void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);
// SetIcon functions. To remove icon, call SetIcon(0)
// 只安装图盘图标
BOOL SetIcon(UINT uID,LPWSTR lpTip=NULL);
// 安装托盘图标和设置提示信息
BOOL SetIcon(HICON hIcon, LPWSTR lpTip);
// 安装托盘图标和设置提示信息
BOOL SetIcon(LPCTSTR lpResName, LPWSTR lpTip)
{
return SetIcon(lpResName ?
AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip);
}
/*
BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCSTR lpTip)
{
return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip);
}
*/
virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
/*
BOOL ShowBalloonTip();
笔者没有实现泡泡其实,如果有想法的可以留言
*/
protected:
NOTIFYICONDATA m_nid;
DECLARE_MESSAGE_MAP()
};
// TrayIcon.h : 头文件
//
// 继承自CCmdTarget才能接收消息,详见《深入浅出 MFC》
class CTrayIcon : public CCmdTarget
{
DECLARE_DYNAMIC(CTrayIcon)
public:
CTrayIcon(UINT uID);
~CTrayIcon();
// Call this to receive tray notifications
void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);
// SetIcon functions. To remove icon, call SetIcon(0)
// 只安装图盘图标
BOOL SetIcon(UINT uID,LPWSTR lpTip=NULL);
// 安装托盘图标和设置提示信息
BOOL SetIcon(HICON hIcon, LPWSTR lpTip);
// 安装托盘图标和设置提示信息
BOOL SetIcon(LPCTSTR lpResName, LPWSTR lpTip)
{
return SetIcon(lpResName ?
AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip);
}
/*
BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCSTR lpTip)
{
return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip);
}
*/
virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);
/*
BOOL ShowBalloonTip();
笔者没有实现泡泡其实,如果有想法的可以留言
*/
protected:
NOTIFYICONDATA m_nid;
DECLARE_MESSAGE_MAP()
};
TrayIcon.cpp
//
TrayIcon.cpp : 实现文件
//
#include " stdafx.h "
#include " TrayIconShell.h "
#include " TrayIcon.h "
/*
注意:托盘菜单的ID要和图标资源的ID一样,否则会出错!
*/
IMPLEMENT_DYNAMIC(CTrayIcon, CCmdTarget)
CTrayIcon::CTrayIcon(UINT uID)
{
::memset(&m_nid, 0, sizeof(m_nid));
m_nid.uID = uID;
m_nid.cbSize = sizeof(NOTIFYICONDATA);
}
CTrayIcon::~CTrayIcon()
{
this->SetIcon( 0);
}
void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
// 断言,正确才中断
ASSERT(pNotifyWnd==NULL
||::IsWindow(pNotifyWnd->GetSafeHwnd()));
m_nid.hWnd = pNotifyWnd->m_hWnd;
ASSERT(uCbMsg == 0||uCbMsg>=WM_USER);
m_nid.uCallbackMessage = uCbMsg;
}
BOOL CTrayIcon::SetIcon(UINT uID,LPWSTR lpTip)
{
HICON hIcon = NULL;
if(uID)
{
m_nid.uID = uID; // 更改当前对象的uID
hIcon = AfxGetApp()->LoadIconW(uID);
}
return SetIcon(hIcon,lpTip);
}
BOOL CTrayIcon::SetIcon(HICON hIcon, LPWSTR lpTip)
{
UINT uMsg;
m_nid.uFlags = 0;
if(hIcon)
{
uMsg = m_nid.hIcon?NIM_MODIFY:NIM_ADD; // 当前如果存在ICON,就修改
m_nid.hIcon = hIcon;
m_nid.uFlags = NIF_ICON; // 设置标志
// 如果存在hIcon才进行设置提示
if(lpTip)
{
::lstrcpyn(m_nid.szTip,(LPCWSTR)lpTip, sizeof(m_nid.szTip));
}
if(m_nid.szTip[ 0])
m_nid.uFlags |= NIF_TIP; // 设置标志
}
else // 删除图标
{
// 如果当前m_nid的hIcon为null,那么直接结束,啥都不用干啦
if(m_nid.hIcon==NULL) return true;
uMsg = NIM_DELETE; // 如果当前m_nid的hIcon不为null,删除图标
m_nid.hIcon = NULL;
}
if(m_nid.uCallbackMessage && m_nid.hWnd)
m_nid.uFlags |= NIF_MESSAGE;
BOOL bRet = ::Shell_NotifyIcon(uMsg,&m_nid);
// 把失败和NIM_DELETE的操作放在这里
if(!bRet) m_nid.hIcon = NULL;
return bRet;
}
LRESULT CTrayIcon::OnTrayNotification(WPARAM uID, LPARAM lEvent)
{
if(uID != m_nid.uID || // 如果uID不属于此对象
(lEvent != WM_RBUTTONUP && lEvent != WM_LBUTTONDBLCLK))
return 0;
CMenu menu; // 知道吗,MFC的封装我觉得在这里已经表现得淋漓尽致了。
if(!menu.LoadMenuW(m_nid.uID))
return 0;
CMenu * pSubMenu = menu.GetSubMenu( 0);
if(!pSubMenu)
return 0;
if(lEvent == WM_RBUTTONUP)
{
CPoint mouse;
::GetCursorPos(&mouse);
::SetForegroundWindow(m_nid.hWnd);
::TrackPopupMenu(pSubMenu->m_hMenu,TRUE,mouse.x,mouse.y, 0,
m_nid.hWnd,NULL);
}
if(lEvent == WM_LBUTTONDBLCLK)
{
::SendMessage(m_nid.hWnd,WM_COMMAND,
pSubMenu->GetMenuItemID( 0), 0);
}
return 1;
}
BEGIN_MESSAGE_MAP(CTrayIcon, CCmdTarget)
END_MESSAGE_MAP()
//
#include " stdafx.h "
#include " TrayIconShell.h "
#include " TrayIcon.h "
/*
注意:托盘菜单的ID要和图标资源的ID一样,否则会出错!
*/
IMPLEMENT_DYNAMIC(CTrayIcon, CCmdTarget)
CTrayIcon::CTrayIcon(UINT uID)
{
::memset(&m_nid, 0, sizeof(m_nid));
m_nid.uID = uID;
m_nid.cbSize = sizeof(NOTIFYICONDATA);
}
CTrayIcon::~CTrayIcon()
{
this->SetIcon( 0);
}
void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg)
{
// 断言,正确才中断
ASSERT(pNotifyWnd==NULL
||::IsWindow(pNotifyWnd->GetSafeHwnd()));
m_nid.hWnd = pNotifyWnd->m_hWnd;
ASSERT(uCbMsg == 0||uCbMsg>=WM_USER);
m_nid.uCallbackMessage = uCbMsg;
}
BOOL CTrayIcon::SetIcon(UINT uID,LPWSTR lpTip)
{
HICON hIcon = NULL;
if(uID)
{
m_nid.uID = uID; // 更改当前对象的uID
hIcon = AfxGetApp()->LoadIconW(uID);
}
return SetIcon(hIcon,lpTip);
}
BOOL CTrayIcon::SetIcon(HICON hIcon, LPWSTR lpTip)
{
UINT uMsg;
m_nid.uFlags = 0;
if(hIcon)
{
uMsg = m_nid.hIcon?NIM_MODIFY:NIM_ADD; // 当前如果存在ICON,就修改
m_nid.hIcon = hIcon;
m_nid.uFlags = NIF_ICON; // 设置标志
// 如果存在hIcon才进行设置提示
if(lpTip)
{
::lstrcpyn(m_nid.szTip,(LPCWSTR)lpTip, sizeof(m_nid.szTip));
}
if(m_nid.szTip[ 0])
m_nid.uFlags |= NIF_TIP; // 设置标志
}
else // 删除图标
{
// 如果当前m_nid的hIcon为null,那么直接结束,啥都不用干啦
if(m_nid.hIcon==NULL) return true;
uMsg = NIM_DELETE; // 如果当前m_nid的hIcon不为null,删除图标
m_nid.hIcon = NULL;
}
if(m_nid.uCallbackMessage && m_nid.hWnd)
m_nid.uFlags |= NIF_MESSAGE;
BOOL bRet = ::Shell_NotifyIcon(uMsg,&m_nid);
// 把失败和NIM_DELETE的操作放在这里
if(!bRet) m_nid.hIcon = NULL;
return bRet;
}
LRESULT CTrayIcon::OnTrayNotification(WPARAM uID, LPARAM lEvent)
{
if(uID != m_nid.uID || // 如果uID不属于此对象
(lEvent != WM_RBUTTONUP && lEvent != WM_LBUTTONDBLCLK))
return 0;
CMenu menu; // 知道吗,MFC的封装我觉得在这里已经表现得淋漓尽致了。
if(!menu.LoadMenuW(m_nid.uID))
return 0;
CMenu * pSubMenu = menu.GetSubMenu( 0);
if(!pSubMenu)
return 0;
if(lEvent == WM_RBUTTONUP)
{
CPoint mouse;
::GetCursorPos(&mouse);
::SetForegroundWindow(m_nid.hWnd);
::TrackPopupMenu(pSubMenu->m_hMenu,TRUE,mouse.x,mouse.y, 0,
m_nid.hWnd,NULL);
}
if(lEvent == WM_LBUTTONDBLCLK)
{
::SendMessage(m_nid.hWnd,WM_COMMAND,
pSubMenu->GetMenuItemID( 0), 0);
}
return 1;
}
BEGIN_MESSAGE_MAP(CTrayIcon, CCmdTarget)
END_MESSAGE_MAP()
使用方法
由于这个类的父类是CCmdTarget,所以,可以接受命令消息(菜单的消息),我们可以自定义添加消息。
在需要使用“托盘”的窗口类中声明一个CTrayIcon的对象,然后在OnCreate函数当中调用即可。
int CTrayIconShellDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == - 1)
return - 1;
// TODO: 在此添加您专用的创建代码
TCHAR tip[ 100] = TEXT( " 捣乱小子 ");
TrayIcon.SetNotificationWnd( this,WM_NOTIFY_MSG);
TrayIcon.SetIcon(IDR_MAINFRAME,tip);
return 0;
}
{
if (CDialog::OnCreate(lpCreateStruct) == - 1)
return - 1;
// TODO: 在此添加您专用的创建代码
TCHAR tip[ 100] = TEXT( " 捣乱小子 ");
TrayIcon.SetNotificationWnd( this,WM_NOTIFY_MSG);
TrayIcon.SetIcon(IDR_MAINFRAME,tip);
return 0;
}
总结
在这一次实验之后,我更加仰慕和崇尚面向对象(OO)的思想,如果编程思路清晰,数据设计得当,接口功能完善,以及接口与接口之间有良好的信息沟通,OO能让你在程序设计当中游刃有余,所以在平时的时候要养成面向对象的思考习惯。
在C++面向对象编程的过程当中,会遇到一些公共的接口,这些接口只完成某些固定的操作,与其他的接口的联系不是非常的紧密的时候,我们可以将此接口设计为static模式,这样就不会让每个对象都担负着那么多的方法了。
本文完 Monday, April 23, 2012(更新)
捣乱小子 http://www.cnblogs.com/daoluanxiaozi/