在MFC框架基础上实现系统托盘与用户的交互
1、以一个SDI(单文档界面)程序为例,讲述在MFC中的实现。利用系统函数Shell_NotifyIcon可以将一个图标显示在任务栏的通告区中。
2、主界面的隐藏:在SDK下是通过修改入口函数WinMain中的API函数CreatWindow的参数来实现的,在MFC下已经对其进行了封装,使我们无法直接对WinMain函数内部进行访问与修改,不过可以通过MFC提供的另外一个接口--在主框架类的预创建窗口函数中设定主框架窗口的风格和扩展风格来实现之:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if ( !CFrameWnd::PreCreateWindow(cs) )
{
return FALSE;
}
cs.style = WS_POPUP;
cs.dwExStyle |= WS_EX_TOOLWINDOW;//在原基础上再添一种WS_EX_TOOLWINDOW即使其不会出现在任务条上
return TRUE;
}
3、将图标放置到托盘区:将图标放到托盘之前必须先将图标装载到内存中,可用API函数LoadIcon实现。之后就可以用系统函数Shell_NotifyIcon将一个图标显示到任务栏的托盘区中了。该函数的原型为:
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid)
其第一个参数用以指定动作,如NIM_ADD将图标装载到托盘中,NIM_DELETE可将装载到托盘中的图标删除掉;其第二个参数是一个具有 NOTIFYICONDATA类型的结构指针。该结构的原型为:
typedef struct _NOTIFYICONDATA
{// nid
DWORD cbSize;
HWND hWnd; //hWnd为接受该图标所发出的消息的窗口的句柄,可以将主框架类的m_hWnd变量传给它
UINT uID; //为被显示图标的ID
UINT uFlags; //指明其余的几个成员(hIcon、uCallBackMessage和szTip)的值是否有效
UINT uCallbackMessage;//uCallbackMessage为一个自定义的消息,当用户在该图标上作用一些鼠标动作时, //将向hWnd 成员中指定的窗口发出该消息,可以定义该消息WM_TRAY为WM_USER+100
HICON hIcon; //hIcon为被显示图标的句柄即前面的LoadIcon的返回值
char szTip[64]; //zTip为一字符数组,当鼠标停留在该图标上时,将其内容显示在浮动的提示信息框中
}NOTIFYICONDATA, *PNOTIFYICONDATA;
.net中定义为:
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (_WIN32_IE < 0x0500)
TCHAR szTip[64];
#else
TCHAR szTip[128];
#endif
#if (_WIN32_IE >= 0x0500)
DWORD dwState;
DWORD dwStateMask;
TCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
} DUMMYUNIONNAME;
TCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (_WIN32_IE >= 0x600)
GUID guidItem;
#endif
} NOTIFYICONDATA, *PNOTIFYICONDATA;
4、上述工作必须在程序一开始执行时就完成,所以在主框架类的OnCreate函数中修改如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
……
m_wndToolBar.EnableDocking( CBRS_ALIGN_ANY );
EnableDocking( CBRS_ALIGN_ANY );
DockControlBar( &m_wndToolBar );
……
//以下添加
NOTIFYICONDATA tnd;
//tnd.cbSize = sizeof( NOTIFYICONDATA );//如果使用这句初始化,BoundsChecker会报错,而应换成下面的大小初始化
tnd.cbSize = NOTIFYICONDATA_V1_SIZE;
tnd.hWnd = this->m_hWnd;
tnd.uID = IDR_MAINFRAME;
tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnd.uCallbackMessage = WM_MYMESSAGE;
tnd.hIcon = LoadIcon( AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME) );
strcpy(tnd.szTip, "托盘演示软件");
Shell_NotifyIcon(NIM_ADD, &tnd);
return 0;
}
(刘树伟注:自定义消息的添加:
1、打开"工程名.h"文件(对话框程序写在stdafx.h中),在
#endif
#define WM_MYMESSAGE (WM_APP + 105)//自定义消息宏(或WM_USER+105)
#include "resource.h" // main symbols
间加入自定义消息,也就是自定义消息宏
2、然后在消息映射中加入
ON_MESSAGE(WM_MYMESSAGE, OnMenu)//OnMenu定义鼠标左击或者右击任务栏中的图标时的响应
3、在CMainFrame类中加入成员函数:
LRESULT OnMenu(WPARAM wParam, LPARAM lParam);
注意加在protected中,前面加上afx_msg前缀
)
5、弹出式菜单的添加及激活
(作者注:其实可以在ResourceView中进行编辑,只要插入新的菜单,然后添加子菜单就可以了)
该菜单的激活只有通过自定义消息WM_MYMESSAGE的响应函数这一条途径来实现,因为当用户在该图标上作用一些鼠标动作时,只会发出这个消息,可以通过判断消息的低位参数lParma来决定是鼠标的何种动作,下面代码使当鼠标右键按下时在鼠标当前位置弹出上述菜单:(添加成员函数的形式添加)
LRESULT CMainFrame::OnMenu(WPARAM wParam,LPARAM lParam)
{
UINT uID = (UINT) wParam;
UINT uMouseMsg = (UINT) lParam;
POINT pt;
CMenu menu;
CMenu *pMenu;
if(WM_RBUTTONDOWN == uMouseMsg)//如果是单击右键
{
switch ( uID )
{
case IDR_MAINFRAME://如果是我们的图标
SetForegroundWindow();
GetCursorPos( &pt );//取得鼠标位置
//执行相应操作
menu.LoadMenu( IDR_MENU1 );
pMenu = menu.GetSubMenu( 0 );
ASSERT (0 != pMenu);
pMenu->TrackPopupMenu(TPM_LEFTALIGN, pt.x, pt.y, this);
PostMessage(WM_NULL, 0, 0);
break;
default:
break;
}
}
return 0;
}
6、图标对外界激励的响应:为使人机界面更友好,使用户在该图标上双击鼠标左键时能弹出程序主界面,再次双击则隐藏之。可以继续在自定义消息WM_MYMESSAGE的响应函数里加入对左键的处理过程:
……
if(WM_LBUTTONDBLCLK == uMouseMsg)//如果是左键双击
{
static bool isMax = false;
switch ( uID )
{
case IDR_MAINFRAME:
if ( !isMax )
{
//显示主窗口
ModifyStyle(WS_POPUP, WS_OVERLAPPEDWINDOW);
ModifyStyleEx(WS_EX_TOOLWINDOW, WS_EX_TOPMOST);
ShowWindow(SW_SHOWMAXIMIZED);
isMax = true;
}
else
{
ShowWindow( SW_HIDE );//隐藏主窗口
ModifyStyle(WS_CAPTION|FWS_PREFIXTITLE|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX, WS_POPUP);
ModifyStyleEx(WS_EX_TOPMOST, WS_EX_TOOLWINDOW);
isMax = false;
}
break;
default:
break;
}
}
……
7、当用右键弹出菜单时,对其菜单项的响应可以使用类向导完成:显示程序主框架的函数
void CMainFrame::OnMainWindow()
{
//显示程序主框架
ModifyStyle(WS_POPUP, WS_OVERLAPPEDWINDOW);
ModifyStyleEx(WS_EX_TOOLWINDOW, WS_EX_TOPMOST);
ShowWindow( SW_SHOWMAXIMIZED );
}
void CMainFrame::OnExit()
{
//退出程序之前用NIM_DELETE属性指定删除掉加入到托盘的图标
NOTIFYICONDATA tnid;
tnid.cbSize = sizeof ( NOTIFYICONDATA );
tnid.hWnd = this->m_hWnd;
tnid.uID = IDR_MAINFRAME;//保证删除的是我们的图标
Shell_NotifyIcon(NIM_DELETE, &tnid);
//退出程序
AfxPostQuitMessage( 0 );
}
小结:
附:对话框程序不出现在任务栏和不出现窗口的方法。对主对话框添加DefWindowProc消息映射。加上:
ShowWindow(SW_HIDE);就可以了。或者用下面更好的方法(注:只能不出现在任务栏,不能隐藏对话框):
在初始化时:
DWORD Style = GetWindowLong( this->m_hWnd, GWL_EXSTYLE );
Style = WS_EX_TOOLWINDOW|WS_EX_DLGMODALFRAME|WS_EX_WINDOWEDGE
|WS_EX_CONTROLPARENT;
SetWindowLong( this->m_hWnd, GWL_EXSTYLE, Style );
并在对话框的扩展属性中钩选Tool Windows项。
更多原创文章请访问:http://www.iuishop.com
本文介绍了在MFC框架基础上实现系统托盘与用户交互的方法。以SDI程序为例,讲述了主界面隐藏、图标放置到托盘区、自定义消息添加、弹出式菜单添加及激活、图标响应外界激励等内容,还给出了对话框程序不出现在任务栏和不出现窗口的方法。
650





