更改窗口图标并将其显示在任务栏
以下两个函数可以为应用程序中的各子窗口显示一个任务条到任务栏并更改它们的图标。对那些象QQ一样隐藏主窗口的应用程序特别有用。
//函数用途:更改一个窗口的图标并将其显示在任务栏、任务切换条、任务管理器里
//参数说明:
//hWnd 要改变图标的窗口句柄
//hLargeIcon 显示到任务切换条上的图标 32*32
//hSmallIcon 显示到除任务切换条之外的图标 16*16
//hIcon 显示的图标,32*32,在显示到任务切换条之外的其余地方时会被自动压缩成16*16的。
//注释:
//此函数对于模式对话框无能为力。
//如果HICON 为NULL,函数不改变窗口图标,但是将原有图标显示到任务栏、
- // 任务切换条、任务管理器里。
- //此函数是通过将窗口的父窗口指针置空来实现将图标显示到任务栏、任务切换条、
- // 任务管理器里的,所以调用完成后,其父窗口指针不再可用。
- BOOL SendWndIconToTaskbar(HWND hWnd,HICON hLargeIcon,HICON hSmallIcon);
- BOOL SendWndIconToTaskbar(HWND hWnd,HICON hIcon);
- BOOL CUIApp::SendWndIconToTaskbar(HWND hWnd,HICON hLargeIcon,HICON hSmallIcon)
- {
- BOOL ret = TRUE;
- ASSERT(hWnd);
- if(!::IsWindow(hWnd))
- return FALSE;
- //获取窗口指针
- CWnd* pWnd;
- pWnd = pWnd->FromHandle(hWnd);
- ASSERT(pWnd);
- if(!pWnd)
- return FALSE;
- //将父窗口设为NULL
- if(pWnd->GetParent())
- if(::SetWindowLong(hWnd,GWL_HWNDPARENT,NULL) == 0)
- return FALSE;
- if(!(pWnd->ModifyStyle(NULL,WS_OVERLAPPEDWINDOW)))
- ret = FALSE;
- //设置窗口图标
- if(hLargeIcon && hSmallIcon)
- {
- pWnd->SetIcon(hSmallIcon,FALSE);
- pWnd->SetIcon(hLargeIcon,TRUE);
- }
- return ret;
- }
- BOOL CUIApp::SendWndIconToTaskbar(HWND hWnd,HICON hIcon)
- {
- BOOL ret = TRUE;
- ASSERT(hWnd);
- if(!::IsWindow(hWnd))
- return FALSE;
- //获取窗口指针
- CWnd* pWnd;
- pWnd = pWnd->FromHandle(hWnd);
- ASSERT(pWnd);
- if(!pWnd)
- return FALSE;
- //将父窗口设为NULL
- if(pWnd->GetParent())
- if(::SetWindowLong(hWnd,GWL_HWNDPARENT,NULL) == 0)
- return FALSE;
- if(!(pWnd->ModifyStyle(NULL,WS_OVERLAPPEDWINDOW)))
- ret = FALSE;
- //设置窗口图标
- pWnd->SetIcon(hIcon,TRUE);
- pWnd->SetIcon(hIcon,FALSE);
- return ret;
- }
如何隐藏应用程序在任务栏上的显示
对于CFrameWnd可以在PreCreateWindow()函数中修改窗口的风格。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |=WS_POPUP;//使主窗口不可见
cs.dwExStyle |=WS_EX_TOOLWINDOW;//不显示任务按钮
return CFrameWnd::PreCreateWindow(cs);
}
对于其他窗口,可以在窗口被Create出来之后ShowWindow之前使用ModifyStyle()和ModifyStyleEx()来修改它的风格。
如何控制窗口框架的最大最小尺寸?
要控制一个框架的的最大最小尺寸,你需要做两件事情。
- 第一步:在CFrameWnd的继承类中处理消息WM_GETMINMAXINFO,结构MINMAXINFO
- 设置了整个窗口类的限制,因此记住要考虑工具条,滚动条等等的大小。
- // 最大最小尺寸的象素点 - 示例
- #define MINX 200
- #define MINY 300
- #define MAXX 300
- #define MAXY 400
- void CMyFrameWnd::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
- {
- CRect rectWindow;
- GetWindowRect(&rectWindow);
- CRect rectClient;
- GetClientRect(&rectClient);
- // get offset of toolbars, scrollbars, etc.
- int nWidthOffset = rectWindow.Width() - rectClient.Width();
- int nHeightOffset = rectWindow.Height() - rectClient.Height();
- lpMMI->ptMinTrackSize.x = MINX + nWidthOffset;
- lpMMI->ptMinTrackSize.y = MINY + nHeightOffset;
- lpMMI->ptMaxTrackSize.x = MAXX + nWidthOffset;
- lpMMI->ptMaxTrackSize.y = MAXY + nHeightOffset;
- }
- 第二步:在CFrameWnd的继承类的PreCreateWindow函数中去掉WS_MAXIMIZEBOX消息,
- 否则在最大化时你将得不到预料的结果.
- BOOL CMyFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
- {
- cs.style &= ~WS_MAXIMIZEBOX;
- return CFrameWnd::PreCreateWindow(cs);
- }
如何修改frame窗口的背景颜色?
MDI窗口的客户区是由frame窗口拥有的另一个窗口覆盖的。为了改变frame窗口背景的颜色,只需要这个客户区的背景颜色就可以了。你必须自己处理WM_ERASEBKND消息。下面是工作步骤:
创建一个从CWnd类继承的类,就叫它CMDIClient吧;
在CMDIFrameWnd中加入CMDIClient变量;(具体情况看下面的代码)
#include "MDIClient.h"
class CMainFrame : public CMDIFrameWnd
{
...
protected:
CMDIClient m_wndMDIClient;
}
重载CMDIFrameWnd::OnCreateClient,下面是这段代码,请注意其中的SubclassWindow();
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
if ( CMDIFrameWnd::OnCreateClient(lpcs, pContext) )
{
m_wndMDIClient.SubclassWindow(m_hWndMDIClient);
return TRUE;
}
else
return FALSE;
}
最后要在CMDIClient中加入处理WM_ERASEBKGND的函数。
如何改变view的背景颜色?
若要改变CView,CFrameWnd或CWnd对象的背景颜色需要处理WM_ERASEBKGND消息,下面就是一个范例代码:
BOOL CSampleView::OnEraseBkgnd(CDC* pDC)
{
//设置brush为希望的背景颜色
CBrush backBrush(RGB(255, 128, 128));
//保存旧的brush
CBrush* pOldBrush = pDC->SelectObject(&backBrush);
CRect rect;
pDC->GetClipBox(&rect);
//画需要的区域
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOldBrush);
return TRUE;
}
若要改变CFromView继承类的背景颜色,下面是一个范例代码:
HBRUSH CMyFormView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
switch (nCtlColor)
{
case CTLCOLOR_BTN:
case CTLCOLOR_STATIC:
{ pDC->SetBkMode(TRANSPARENT);
//不加任何处理或设置背景为透明
}
case CTLCOLOR_DLG:
{
CBrush* back_brush;
COLORREF color;
color = (COLORREF) GetSysColor(COLOR_BTNFACE);
back_brush = new CBrush(color);
return (HBRUSH) (back_brush->m_hObject);
}
}
return(CFormView::OnCtlColor(pDC, pWnd, nCtlColor));
}
在任务栏状态区如何显示应用程序图标
有关的数据由NOTIFYICONDATA结构描述:
typedef struct _NOTIFYICONDATA
{
DWORD cbSize; //结构的大小,必须设置
HWND hWnd; //接受回调消息的窗口的句柄
UINT uID; //应用程序定义的图标标志
UINT uFlags; //标志,可以是NIF_ICON、NIF_MESSAGE、NIF_TIP或其组合
UINT uCallbackMessage;//应用程序定义的回调消息标志
HICON hIcon; //图标句柄
char szTip[64]; //提示字串
} NOTIFYICONDATA, *PNOTIFYICONDATA;
函数说明
由Shell_NotifyIcon()函数向系统发送添加、删除、更改图标的消息。
WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(DWORD dwMessage,PNOTIFYICONDATA pnid);
DwMessage为所发送消息的标志:
NIM_ADD 添加图标到任务栏通知区;
NIM_DELETE 删除任务栏通知区的图标;
NIM_MODIFY 更改任务栏通知区的图标、回调消息标志、回调窗口句柄或提示字串;
pnid为NOTIFYICONDATA结构的指针。
回调信息的获得及处理
如果一个任务栏图标有应用程序定义的回调消息,那么当这个图标有鼠标操作时,系统将给hWnd所标志的窗口发送下列的消息:
messageID = uCallbackMessage
wParam = uID
lParam = mouse event(例如WM_LBUTTONDOWN)
通过这种方式,系统通知应用程序用户对图标的操作。如果一个应用程序生成了两个以上的图标,那么你可以根据wParam来判断是哪个图标返回的鼠标操作。通常,标准的Win95任务栏图标有以下鼠标操作响应:
当鼠标停留在图标上时,系统应显示提示信息tooltip;
当使用鼠标右键单击图标时,应用程序应显示快捷菜单;
当使用鼠标左键双击图标时,应用程序应执行快捷菜单的缺省菜单项。
在Microsoft Windows环境中,0x8000到0xBFFF的消息是保留的,应用程序可以定义自定义消息。
关于消息处理的详细内容,请参考下一部分。
源码及实现
在本文中关于任务栏图标的类叫做CTrayIcon,这个类由CCmdTarget(或CObject)类派生,它有如下的成员变量和成员函数:
// TrayIcon.h
// CTrayIcon command target
class CTrayIcon : public CCmdTarget
{
public:
NOTIFYICONDATA m_nid;//NOTIFYICONDATA结构,你的图标要用的啊
BOOL m_IconExist;//标志,看看图标是不是已经存在了
CWnd* m_NotificationWnd;//接受回调消息的窗口,有它就不必经常AfxGetMainWnd了
public:
CWnd* GetNotificationWnd() const;//得到m_NotificationWnd
BOOL SetNotificationWnd(CWnd* pNotifyWnd);//设置(更改)m_NotificationWnd
CTrayIcon();//构造函数
virtual ~CTrayIcon();//析构函数
BOOL CreateIcon(CWnd* pNotifyWnd, UINT uID, HICON hIcon,
LPSTR lpszTip, UINT CallBackMessage);//在任务栏上生成图标
BOOL DeleteIcon();//删除任务栏上的图标
virtual LRESULT OnNotify(WPARAM WParam, LPARAM LParam);//消息响应函数
BOOL SetTipText(UINT nID);//设置(更改)提示字串
BOOL SetTipText(LPCTSTR lpszTip);//设置(更改)提示字串
BOOL ChangeIcon(HICON hIcon);//更改图标
BOOL ChangeIcon(UINT nID);//更改图标
BOOL ChangeIcon(LPCTSTR lpszIconName);//更改图标
BOOL ChangeStandardIcon(LPCTSTR lpszIconName);//更改为标准图标
......
};
下面是成员函数的定义:
// TrayIcon.cpp
// CTrayIcon
CTrayIcon::CTrayIcon()
{//初始化参数
m_IconExist = FALSE;
m_NotificationWnd = NULL;
memset(&m_nid, 0, sizeof(m_nid));
m_nid.cbSize = sizeof(m_nid);//这个参数不会改变
}
CTrayIcon::~CTrayIcon()
{
if (m_IconExist)
DeleteIcon();//删除图标
}
BOOL CTrayIcon::CreateIcon(CWnd* pNotifyWnd, UINT uID, HICON hIcon,
LPSTR lpszTip, UINT CallBackMessage)
{
//确定接受回调消息的窗口是有效的
ASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->GetSafeHwnd()));
ASSERT(CallBackMessage >= WM_USER);//确定回调消息不发生冲突
ASSERT(_tcslen(lpszTip) <= 64);//提示字串不能超过64个字符
m_NotificationWnd = pNotifyWnd;//获得m_NotificationWnd
//设置NOTIFYICONDATA结构
m_nid.hWnd = pNotifyWnd->GetSafeHwnd();
m_nid.uID = uID;
m_nid.hIcon = hIcon;
m_nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
m_nid.uCallbackMessage = CallBackMessage;
//设置NOTIFYICONDATA结构的提示字串
if (lpszTip)
lstrcpyn(m_nid.szTip, lpszTip, sizeof(m_nid.szTip));
else
m_nid.szTip[0] = '\0';
//显示图标
m_IconExist = Shell_NotifyIcon(NIM_ADD, &m_nid);
return m_IconExist;
}
BOOL CTrayIcon::DeleteIcon()
{//删除图标
if (!m_IconExist)
return FALSE;
m_IconExist = FALSE;
return Shell_NotifyIcon(NIM_DELETE, &m_nid);
}
LRESULT CTrayIcon::OnNotify(WPARAM WParam, LPARAM LParam)
{//处理图标返回的消息
if (WParam != m_nid.uID)//如果不是该图标的消息则迅速返回
return 0L;
//准备快捷菜单
CMenu menu;
if (!menu.LoadMenu(IDR_POPUP))//你必须确定资源中有ID为IDR_POPUP的菜单
return 0;
CMenu* pSubMenu = menu.GetSubMenu(0);//获得IDR_POPUP的子菜单
if (!pSubMenu)
return 0;
if (LParam == WM_RBUTTONUP)
{//右键单击弹出快捷菜单
//设置第一个菜单项为缺省
::SetMenuDefaultItem(pSubMenu->m_hMenu, 0, TRUE);
CPoint pos;
GetCursorPos(&pos);
//显示并跟踪菜单
m_NotificationWnd->SetForegroundWindow();
pSubMenu->TrackPopupMenu(TPM_RIGHTALIGN|TPM_LEFTBUTTON
|TPM_RIGHTBUTTON, pos.x, pos.y, m_NotificationWnd, NULL);
}
else if (LParam == WM_LBUTTONDOWN)
{//左键单击恢复窗口
m_NotificationWnd->ShowWindow(SW_SHOW);//恢复窗口
m_NotificationWnd->SetForegroundWindow();//放置在前面
}
else if (LParam == WM_LBUTTONDBLCLK)
{//左键双击执行缺省菜单项
m_NotificationWnd->SendMessage(WM_COMMAND,
pSubMenu->GetMenuItemID(0), 0);
}
return 1L;
}
BOOL CTrayIcon::SetTipText(LPCTSTR lpszTip)
{//设置提示文字
if (!m_IconExist)
return FALSE;
_tcscpy(m_nid.szTip, lpszTip);
m_nid.uFlags |= NIF_TIP;
return Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}
BOOL CTrayIcon::SetTipText(UINT nID)
{//设置提示文字
CString szTip;
VERIFY(szTip.LoadString(nID));
return SetTipText(szTip);
}
BOOL CTrayIcon::ChangeIcon(HICON hIcon)
{//更改图标
if (!m_IconExist)
return FALSE;
m_nid.hIcon = hIcon;
m_nid.uFlags |= NIF_ICON;
return Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}
BOOL CTrayIcon::ChangeIcon(UINT nID)
{//更改图标
HICON hIcon = AfxGetApp()->LoadIcon(nID);
return ChangeIcon(hIcon);
}
BOOL CTrayIcon::ChangeIcon(LPCTSTR lpszIconName)
{//更改图标
HICON hIcon = AfxGetApp()->LoadIcon(lpszIconName);
return ChangeIcon(hIcon);
}
BOOL CTrayIcon::ChangeStandardIcon(LPCTSTR lpszIconName)
{//更改为标准图标
HICON hIcon = AfxGetApp()->LoadStandardIcon(lpszIconName);
return ChangeIcon(hIcon);
}
BOOL CTrayIcon::SetNotificationWnd(CWnd * pNotifyWnd)
{//设置接受回调消息的窗口
if (!m_IconExist)
return FALSE;
//确定窗口是有效的
ASSERT(pNotifyWnd && ::IsWindow(pNotifyWnd->GetSafeHwnd()));
m_NotificationWnd = pNotifyWnd;
m_nid.hWnd = pNotifyWnd->GetSafeHwnd();
m_nid.uFlags |= NIF_MESSAGE;
return Shell_NotifyIcon(NIM_MODIFY, &m_nid);
}
CWnd* CTrayIcon::GetNotificationWnd() const
{//返回接受回调消息的窗口
return m_NotificationWnd;
}
三点补充:
关于使用回调消息的补充说明:
首先,在MainFrm.cpp中加入自己的消息代码;
// MainFrm.cpp : implementation of the CMainFrame class
//
#define MYWM_ICONNOTIFY WM_USER + 10//定义自己的消息代码
第二步增加消息映射和函数声明,对于自定义消息不能由ClassWizard添加消息映射,只能手工添加。
// MainFrm.cpp : implementation of the CMainFrame class
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
//其他的消息映射
......
//}}AFX_MSG_MAP
ON_MESSAGE(WM_ICONNOTIFY,OnNotify)
END_MESSAGE_MAP()
并且在头文件中添加函数声明
// MainFrm.h
afx_msg LRESULT OnNotify(WPARAM WParam, LPARAM LParam);
第三步增加消息处理函数定义
LRESULT CMainFrame::OnNotify(WPARAM WParam, LPARAM LParam)
{
return trayicon.OnNotify(WParam, LParam);//调用CTrayIcon类的处理函数
}
如何隐藏任务栏上的按钮
可以使用下列两种方法:
1.在CreateWindowEx函数中使用WS_EX_TOOLWINDOW窗口式样(相反的如果要确保应用程序在任务栏上生成按钮,可 以使用WS_EX_APPWINDOW窗口式 样)。 The problem with this is that the window decorations are as for a small floating toolbar,
which isn't normally what's wanted.
2.生成一个空的隐藏的top-level窗口,并使其作为可视窗口的父窗口。
3.在应用程序的InitInstance()函数中使用SW_HIDE式样调用ShowWindow()函数。
//pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->ShowWindow(SW_HIDE);
pMainFrame->UpdateWindow();
如何动画任务栏上的图标
在TrayIcon类中加入下列两个函数:
BOOL CTrayIcon::SetAnimateIcons(HICON* hIcon, UINT Number)
{//设置动画图标
ASSERT(Number >= 2);//图标必须为两个以上
ASSERT(hIcon);//图标必须不为空
m_AnimateIcons = new HICON[Number];
CopyMemory(m_AnimateIcons, hIcon, Number * sizeof(HICON));
m_AnimateIconsNumber = Number;
return TRUE;
}
BOOL CTrayIcon::Animate(UINT Index)
{//动画TrayIcon
UINT i = Index % m_AnimateIconsNumber;
return ChangeIcon(m_AnimateIcons[i]);
}
怎样在应用程序中添加相应的菜单和函数
void CMainFrame::OnMenuAnimate()
{//动画TrayIcon,设置图标及定时器
SetTimer(1, 500, NULL);
HICON hIcon[3];
hIcon[0] = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
hIcon[1] = AfxGetApp()->LoadIcon(IDR_MYTURNTYPE);
hIcon[2] = AfxGetApp()->LoadStandardIcon(IDI_HAND);
trayicon.SetAnimateIcons(hIcon, 3);
}
void CMainFrame::OnTimer(UINT nIDEvent)
{//动画TrayIcon
UINT static i;
i += 1;
trayicon.Animate(i);
CMDIFrameWnd::OnTimer(nIDEvent);
}