系统托盘功能如下图所示,是指在操作系统工具栏右侧下方显示的应用图标所承载的部分系统功能。
系统托盘的主要功能包含:创建托盘图标、响应托盘事件、删除托盘图标等功能。下面我们来逐步完成相关功能的实现。
系统托盘一般在窗口主程序最小化时出现,在窗口主程序恢复时删除,此次我们先通过菜单操作来实现显示和删除系统托盘图标的功能,最后再讲解Windows系统菜单消息如何截获处理。
创建/删除托盘图标
首先增加一个菜单菜单项
修改菜单项变量名称
添加菜单项响应函数
菜单响应函数代码如下:
#include <shellapi.h>
#include <wx/app.h>
NOTIFYICONDATA nid;
wxIcon ticon;
#define WM_TO_TRAY WM_USER+100
static const char *graph[] =
{
// columns rows colors chars-per-pixel
"16 16 4 2",
" c black",
". c #BA1825",
"X c gray100",
"UX c None",
// pixels
"UX. . . . . . . . . . . . . . UX",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . X X . . . . X X . . . ",
". . . . . X X . . . . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . X X X X X X X X X X . . . ",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . . . . . . ",
"UX. . . . . . . . . . . . . . UX"
};
void SystemPanel::Onm_SystemTrayIconSelected(wxCommandEvent& event)
{
if(0==nid.uCallbackMessage)
{
//初始化系统托盘
nid.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
nid.hWnd = wxTheApp->GetTopWindow()->GetHandle();
nid.uID = wxTheApp->GetTopWindow()->GetId () ;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_TO_TRAY;//自定义的消息 处理托盘图标事件
ticon=wxIcon( graph );
nid.hIcon = (HICON)(ticon.GetHandle());
wcscpy_s(nid.szTip, wxString("SDK学习").t_str());//鼠标放在托盘图标上时显示的文字
}
if(event.IsChecked())
{
Shell_NotifyIcon(NIM_ADD, &nid);//在托盘区添加图标
}
else
{
Shell_NotifyIcon(NIM_DELETE, &nid);//在托盘区删除图标
}
}
响应托盘事件
系统托盘事件一般只有程序主窗口才可以响应,我们需要通过主窗口传递事件消息到SystemPanel的响应函数中处理。下面我们来讲解如何在Systempanel中响应托盘消息:
首先为托盘创建菜单:
添加菜单项
增加菜单响应函数
在主窗口中重载 MSWWindowProc函数
MSWWindowProc函数代码如下:
#include "SystemPanel.h"
//处理系统消息和自定义消息
WXLRESULT SDKLearnFrame::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
switch (message)
{
//处理自定义托盘消息
case WM_TO_TRAY:
{
m_SystemPanel->WndProc(message,wParam,lParam);
break;
}
}
return wxFrame::MSWWindowProc(message, wParam, lParam);
}
在SystemPanel头文件定义托盘处理函数WndProc
托盘事件处理函数WndProc的实现代码如下:
#include <shellapi.h>
#include <wx/app.h>
#include <iostream>
NOTIFYICONDATA nid;
wxIcon ticon;
static const char *graph[] =
{
// columns rows colors chars-per-pixel
"16 16 4 2",
" c black",
". c #BA1825",
"X c gray100",
"UX c None",
// pixels
"UX. . . . . . . . . . . . . . UX",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . X X . . . . X X . . . ",
". . . . . X X . . . . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . X X X X X X X X X X . . . ",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . . . . . . ",
"UX. . . . . . . . . . . . . . UX"
};
using namespace std;
//处理系统消息和自定义消息
void SystemPanel::WndProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
switch (message)
{
//处理托盘自定义消息
case WM_TO_TRAY:
{
switch(lParam)
{
case WM_RBUTTONDOWN:
{
//托盘消息中lParam接受鼠标的行为
cout<<"message: 选中托盘右键!"<<message<<endl;
POINT point;
GetCursorPos(&point);
SetForegroundWindow(wxTheApp->GetTopWindow()->GetHandle());
TrackPopupMenu((HMENU)m_TrayMenu.GetHMenu(),TPM_LEFTALIGN,point.x,point.y,0,this->GetHandle(),NULL);
}
break;
default:
break;
}
}
break;
default:
break;
}
}
托盘菜单处理函数代码如下:
//通过托盘图标菜单显示窗口
void SystemPanel::Onm_ShowMainWndSelected(wxCommandEvent& event)
{
wxTheApp->GetTopWindow()->Enable();//保证窗口可以接受窗口消息
bool hidewnd=IsWindowVisible(wxTheApp->GetTopWindow()->GetHandle());
if(hidewnd)
{
ShowWindow(wxTheApp->GetTopWindow()->GetHandle(), SW_HIDE);
}
else
{
ShowWindow(wxTheApp->GetTopWindow()->GetHandle(), SW_SHOW);
SetForegroundWindow(wxTheApp->GetTopWindow()->GetHandle());
Shell_NotifyIcon(NIM_DELETE, &nid);//在托盘区删除图标
}
}
//通过托盘图标菜单退出软件
void SystemPanel::Onm_QuitWndSelected(wxCommandEvent& event)
{
Shell_NotifyIcon(NIM_DELETE, &nid);//在托盘区删除图标
exit(0);
}
至此,我们实现了托盘菜单的简单控制。
但是在实际应用中,我们通常是通过窗口最小化按钮来控制显示系统托盘并隐藏程序主窗口,用双击托盘图标来显示程序主窗口并删除托盘图标。下面我们来讲解如何实现。
首先,我们在主窗口MSWWindowProc函数中添加WM_SYSCOMMAND系统菜单消息处理,最小化按钮消息SC_MINIMIZE,代码如下:
//处理系统消息和自定义消息
WXLRESULT SDKLearnFrame::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
switch (message)
{
//处理自定义托盘消息
case WM_TO_TRAY:
{
m_SystemPanel->WndProc(message,wParam,lParam);
}
break;
//处理窗口最小化消息
case WM_SYSCOMMAND:
{
switch(wParam)
{
//处理最小化窗口
case SC_MINIMIZE:
{
m_SystemPanel->WndProc(message,wParam,lParam);
}
break;
default:
break;
}
}
break;
}
return wxFrame::MSWWindowProc(message, wParam, lParam);
}
然后,我们在SystemPanel的WndProc函数中添加具体的处理过程,代码如下:
#include <shellapi.h>
#include <wx/app.h>
#include <iostream>
NOTIFYICONDATA nid;
wxIcon ticon;
static const char *graph[] =
{
// columns rows colors chars-per-pixel
"16 16 4 2",
" c black",
". c #BA1825",
"X c gray100",
"UX c None",
// pixels
"UX. . . . . . . . . . . . . . UX",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . . . . . . . X X . . . ",
". . . . . X X . . . . X X . . . ",
". . . . . X X . . . . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . . . X X . X X . X X . . . ",
". . . X X X X X X X X X X . . . ",
". . . . . . . . . . . . . . . . ",
". . . . . . . . . . . . . . . . ",
"UX. . . . . . . . . . . . . . UX"
};
using namespace std;
//处理系统消息和自定义消息
void SystemPanel::WndProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
{
switch (message)
{
//处理托盘自定义消息
case WM_TO_TRAY:
{
switch(lParam)
{
case WM_RBUTTONDOWN:
{
//托盘消息中lParam接受鼠标的行为
cout<<"message: 选中托盘右键!"<<message<<endl;
POINT point;
GetCursorPos(&point);
SetForegroundWindow(wxTheApp->GetTopWindow()->GetHandle());
TrackPopupMenu((HMENU)m_TrayMenu.GetHMenu(),TPM_LEFTALIGN,point.x,point.y,0,this->GetHandle(),NULL);
}
break;
case WM_LBUTTONDBLCLK:
{
cout<<"message: 选中托盘左键双击,显示主窗口!"<<message<<endl;
wxTheApp->GetTopWindow()->Enable();//保证窗口可以接受窗口消息
bool hidewnd=IsWindowVisible(wxTheApp->GetTopWindow()->GetHandle());
if(hidewnd)
{
ShowWindow(wxTheApp->GetTopWindow()->GetHandle(), SW_HIDE);
}
else
{
ShowWindow(wxTheApp->GetTopWindow()->GetHandle(), SW_SHOW);
SetForegroundWindow(wxTheApp->GetTopWindow()->GetHandle());
Shell_NotifyIcon(NIM_DELETE, &nid);//在托盘区删除图标
}
}
break;
default:
break;
}
}
break;
//处理窗口最小化消息
case WM_SYSCOMMAND:
{
switch(wParam)
{
//截取窗口消息 最小化窗口
case SC_MINIMIZE:
{
if(0==nid.uCallbackMessage)
{
//初始化系统托盘
nid.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
nid.hWnd = wxTheApp->GetTopWindow()->GetHandle();
nid.uID = wxTheApp->GetTopWindow()->GetId () ;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_TO_TRAY;//自定义的消息 处理托盘图标事件
ticon=wxIcon( graph );
nid.hIcon = (HICON)(ticon.GetHandle());
wcscpy_s(nid.szTip, wxString("SDK学习").t_str());//鼠标放在托盘图标上时显示的文字
}
Shell_NotifyIcon(NIM_ADD, &nid);//在托盘区添加图标
ShowWindow(wxTheApp->GetTopWindow()->GetHandle(), SW_HIDE);//保证隐藏主窗口后,无法接受窗口消息 最小化消息
wxTheApp->GetTopWindow()->Disable();
cout<<_("message: 窗口最小了!")<<lParam<<endl;
}
break;
default:
break;
}
}
break;
default:
break;
}
}
最后,编译并运行程序,就可以看到相关效果了!
至此,完整的系统托盘实现完成。