Visual C++窗体设计技巧集

 

[前言:]有好的界面软件就成功了一半,本文将向您介绍怎样设计一些有“稀奇古怪”形状的窗体,如何设定窗体的颜色、如何设置任务栏和状态栏以及菜单图标等等,通过这些技巧能更深入的理解VC的文档-视图结构。

  如何制作透明窗体
  使用SetLayeredWindowAttributes可以方便的制作透明窗体,此函数在w2k以上才支持,而且如果希望直接使用的话,可能需要下载最新的SDK。不过此函数在w2k的user32.dll里有实现,所以如果你不希望下载巨大的sdk的话,可以直接使用GetProcAddress获取该函数的指针。


  SetLayeredWindowAttributes的函数原型如下:

BOOL SetLayeredWindowAttributes(
HWND hwnd, // handle to the layered window
COLORREF crKey, // specifies the color key
BYTE bAlpha, // value for the blend function
DWORD dwFlags // action
);


Windows NT/2000/XP: Included in Windows 2000 and later.
Windows 95/98/Me: Unsupported.(注意了,在win9x里没法使用的)
Header: Declared in Winuser.h; include Windows.h.
Library: Use User32.lib.


  一些常量:


WS_EX_LAYERED = 0x80000;
LWA_ALPHA = 0x2;
LWA_COLORKEY=0x1;

  其中dwFlags有LWA_ALPHA和LWA_COLORKEY

  LWA_ALPHA被设置的话,通过bAlpha决定透明度.

  LWA_COLORKEY被设置的话,则指定被透明掉的颜色为crKey,其他颜色则正常显示.

  要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性(旧的sdk没有定义这个属性,所以可以直接指定为0x80000).

  例子代码:

  在OnInitDialog()加入:

//加入WS_EX_LAYERED扩展属性
SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,
GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0x80000);
HINSTANCE hInst = LoadLibrary("User32.DLL");
if(hInst)
{
 typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
 MYFUNC fun = NULL;
 //取得SetLayeredWindowAttributes函数指针
 fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
 if(fun)fun(this->GetSafeHwnd(),0,128,2);
 FreeLibrary(hInst);
}

  稍加修改还可以作出淡出淡入的效果. 注意第三个参数(128)不要取得太小了,为0的话就完全透明,看不到了。

  如何使框架窗口的图标为动画显示

  可以用TIMER,但是TIMER不能有效的定时。因为TIMER发送的是窗口消息,当窗口忙于处理键盘、鼠标等消息时就不能及时处理TIMER,会使间隔时间变得很长 。

  可以考虑用一个单独得TIMER线程,用Sleep()定时来解决此问题。


UINT Timer(LPVOID param)
{
 HWND hWnd=(HWND)param;
 while(1)
 {
  Sleep(ms);
  PostMessage(hWnd,CH_PICTURE,NULL,NULL)
 }
}

  Sleep(ms)后发送自定义消息。消息处理函数就选择某一个ICON或BITMAP来显示。如 :


MyBotton.SetBitmap((HBITMAP)Bitmap[i]);

  Bitmap是一个位图数组,存放有j个位图。消息处理函数运行一次,i就累加一次,当i==j时,i就回到0;

  防止窗口闪烁的方法

  1、将Invalidate()替换为InvalidateRect()。

  Invalidate()会导致整个窗口的图象重画,需要的时间比较长,而InvalidateRect()仅仅重画Rect 区域内的内容,所以所需时间会少一些。虫虫以前很懒,经常为一小块区域的重画就调用Invalidate(),不愿意自己去计算需要重画的Rect,但是事实是,如果你确实需要改善闪烁的情况,计算一个Rect所用的时间比起重画那些不需要重画的内容所需要的时间要少得多。

  2、禁止系统搽除你的窗口。

  系统在需要重画窗口的时候会帮你用指定的背景色来搽除窗口。可是,也许需要重画的区域也许非常小。或者,在你重画这些东西之间还要经过大量的计算才能开始。这个时候你可以禁止系统搽掉原来的图象。直到你已经计算好了所有的数据,自己把那些需要搽掉的部分用背景色覆盖掉(如:dc.FillRect(rect,&brush);rect是需要搽除的区域,brush是带背景色的刷子),再画上新的图形。要禁止系统搽除你的窗口,可以重载OnEraseBkgnd()函数,让其直接返回TRUE就可以了。如

BOOL CMyWin::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
//return CWnd::OnEraseBkgnd(pDC);//把系统原来的这条语句注释掉。
}

  3、有效的进行搽除。

  搽除背景的时候,不要该搽不该搽的地方都搽。比如,你在一个窗口上放了一个很大的Edit框,几乎占了整个窗口,那么你频繁的搽除整个窗口背景将导致Edit不停重画形成剧烈的闪烁。事实上你可以CRgn创建一个需要搽除的区域,只搽除这一部分。如

GetClientRect(rectClient);
rgn1.CreateRectRgnIndirect(rectClient);
rgn2.CreateRectRgnIndirect(m_rectEdit);
if(rgn1.CombineRgn(&rgn1,&rgn2,RGN_XOR) == ERROR)//处理后的rgn1只包括了Edit框之外的客户区域,这样,Edit将不会被我的背景覆盖而导致重画。
{
ASSERT(FALSE);
return ;
}
brush.CreateSolidBrush(m_clrBackgnd);
pDC->FillRgn(&rgn1,&brush);
brush.DeleteObject();

  注意:在使用这个方法的时候要同时使用方法二。别忘了,到时候又说虫虫的办法不灵。

  4、使用MemoryDC先在内存里把图画好,再复制到屏幕上。

  这对于一次画图过程很长的情况比较管用。毕竟内存操作比较快,而且复制到屏幕又是一次性的,至少不会出现可以明显看出一个东东从左画到右的情况。

void CMyWin::OnPaint()
{
CPaintDC dc1(this); // device context for painting
dcMemory.CreateCompatibleDC(&dc1);
CBitmap bmp;//这里的Bitmap是必须的,否则当心弄出一个大黑块哦。
bmp.CreateCompatibleBitmap(&dc1,rectClient.Width(),rectClient.Height());
dcMemory.SelectObject(&bmp);

//接下来你想怎么画就怎么画吧。
//dcMemory.FillRect(rectClient,&brush);

dc1.BitBlt(0,0,rectClient.Width(),rectClient.Height(),&dcMemory,0,0,SRCCOPY);
dcMemory.DeleteDC();
// Do not call CWnd::OnPaint() for painting messages
}

  如何实现全屏显示

  全屏显示是一些应用软件程序必不可少的功能。比如在用VC++编辑工程源文件或编辑对话框等资源时,选择菜单“ViewFull Screen”,即可进入全屏显示状态,按“Esc”键后会退出全屏显示状态。

  在VC++6.0中我们用AppWizard按默认方式生成单文档界面的应用程序框架。下面将先讨论点击菜单项“ViewFull Screen”实现全屏显示的方法,再讲述按“Esc”键后如何退出全屏显示状态。

  1) 在CMainFrame类中,增加如下三个成员变量。

  private:
    WINDOWPLACEMENT m_OldWndPlacement; //用来保存原窗口位置
    BOOL m_bFullScreen; //全屏显示标志
    CRect m_FullScreenRect; //表示全屏显示时的窗口位置 

  2)在资源编辑器中编辑菜单IDR_MAINFRAME。在“View”菜单栏下添加菜单项“Full Screen”。在其属性框中,ID设置为ID_FULL_SCREEN,Caption为“Full Screen”。还可以在工具栏中添加新的工具图标,并使之与菜单项 “Full Screen”相关联,即将其ID值也设置为ID_FULL_SCREEN。

  3)设计全屏显示处理函数,在CMainFrame类增加上述菜单项ID_FULL_SCREEN消息的响应函数。响应函数如下:


  void CMainFrame::OnFullScreen()
  {

GetWindowPlacement(&m_OldWndPlacement);
   CRect WindowRect;
   GetWindowRect(&WindowRect);
   CRect ClientRect;
   RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery, &ClientRect);
   ClientToScreen(&ClientRect);
   // 获取屏幕的分辨率
   int nFullWidth=GetSystemMetrics(SM_CXSCREEN);
   int nFullHeight=GetSystemMetrics(SM_CYSCREEN);
   // 将除控制条外的客户区全屏显示到从(0,0)到(nFullWidth, nFullHeight)区域, 将(0,0)和(nFullWidth, nFullHeight)两个点外扩充原窗口和除控制条之外的 客户区位置间的差值, 就得到全屏显示的窗口位置
   m_FullScreenRect.left=WindowRect.left-ClientRect.left;
   m_FullScreenRect.top=WindowRect.top-ClientRect.top;
   m_FullScreenRect.right=WindowRect.right-ClientRect.right+nFullWidth;
   m_FullScreenRect.bottom=WindowRect.bottom-ClientRect.bottom+nFullHeight;
   m_bFullScreen=TRUE; // 设置全屏显示标志为 TRUE
   // 进入全屏显示状态
   WINDOWPLACEMENT wndpl;
   wndpl.length=sizeof(WINDOWPLACEMENT);
   wndpl.flags=0;
   wndpl.showCmd=SW_SHOWNORMAL;
   wndpl.rcNormalPosition=m_FullScreenRect;
   SetWindowPlacement(&wndpl);

}


  4)重载CMainFrame类的OnGetMinMaxInfo函数,在全屏显示时提供全屏显示的位置信息。


  void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
  {

if(m_bFullScreen)
   {

lpMMI->ptMaxSize.x=m_FullScreenRect.Width();
   lpMMI->ptMaxSize.y=m_FullScreenRect.Height();
   lpMMI->ptMaxPosition.x=m_FullScreenRect.Width();
   lpMMI->ptMaxPosition.y=m_FullScreenRect.Height();
   //最大的Track尺寸也要改变
   lpMMI->ptMaxTrackSize.x=m_FullScreenRect.Width();
   lpMMI->ptMaxTrackSize.y=m_FullScreenRect.Height();
   }

CFrameWnd::OnGetMinMaxInfo(lpMMI) ;
  }


  完成上面的编程后,可以联编执行FullScreen.exe,选择菜单“ViewFull Screen”或点击与之关联的工具栏按钮即可进入全屏显示状态。但现在还需要增加用户退出全屏显示状态的操作接口,下面讲述如何编程实现按“Esc”键退出全屏显示状态。

  1)在ClassView中选中CMainFrame并单击鼠标右键,选择“Add Member Function...”,添加public类型的成员函数EndFullScreen,该函数将完成退出全屏显示的操作。


  void CMainFrame::EndFullScreen()
  {

if(m_bFullScreen)
   {// 退出全屏显示, 恢复原窗口显示
  ShowWindow(SW_HIDE);
   SetWindowPlacement(&m_OldWndPlacement);

}

ζ# 125;


  2)函数EndFullScreen可以退出全屏显示状态,问题是如何在“Esc”键被按下之后调用执行此函数。由于视图类可以处理键盘输入的有关消息(如WM_KEYDOWN表示用户按下了某一个键),我们将在视图类CFullScreenView中添加处理按键消息WM_KEYDOWN的响应函数OnKeyDown。判断如果按的键为“Esc”键,则调用CMainFrame类的函数EndFullScreen,便可退出全屏显示状态。


  void CFullScreenView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  {

if(nChar==VK_ESCAPE) // 如果按的键为Esc键
   {// 获取主框架窗口的指针

   CMainFrame *pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;
   // 调用主窗口类的自定义函数 EndFullScreen ,便可退出全屏显示状态
  pFrame->EndFullScreen();

}
  CView::OnKeyDown(nChar, nRepCnt, nFlags);

}


  更改窗口图标并将其显示在任务栏

  以下两个函数可以为应用程序中的各子窗口显示一个任务条到任务栏并更改它们的图标。对那些象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 hIco n = 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);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录回到顶部↑丛书前言 前言 读者须知 第1章 visual c++起点 实例1 mfc框架、文档和视 实例2 创建多文档窗口 实例3 创建对话框窗口 实例4 创建启动窗口 第2章 多窗口界面 实例5 多窗口的单文档界面 实例6 通过菜单改变视图 实例7 通过工具栏改变视图 实例8 多视图窗口数据显示 第3章 实现microsoft风格 实例9 visual studio风格的窗口 实例10 internetexplorer4.0风格的窗口 实例11 outlook风格的窗口 实例12 状态栏中的任务栏 第4章 超文本界面 实例13 创建超级链接界面 . 实例14 显示模式html对话框窗口 实例15 超文本浏览器 实例16 数字化时钟 第5章 各种特效窗口 实例17 创建不规则窗口 实例18 操作dib位图窗口 实例19 位图转换窗口 实例20 渐变的窗口 第6章 对话框界面 实例21 可扩展对话柜 实例22 位图预览对话框 实例23 显示目录树对话框 实例24 透明对话框 第7章 菜单状态操作 实例25 漂亮的图文菜单 实例26 随文档动态改变菜单 实例27 启用和禁止菜单命令 实例28 复选菜单命令 第8章 自绘制菜单 实例29 自绘制图标菜单 实例30 visualstudio外观的工具栏 实例31 对话框窗口中的菜单 实例32 弹出位图菜单 第9章 动画按钮 实例33 avi动画按钮 实例34 实现图标按钮 实例35 实现一组图标按钮 实例36 绘制dib动画按钮 第10章 多彩按钮 实例37 位图按钮 实例38 带颜色的按钮 实例39 对鼠标敏感的按钮 实例40 可弹出菜单的按钮 第11章 图文按钮风格大全 实例41 各种图文按钮风格 实例42 图标浮动按钮 实例43 图钉按钮 实例44 计数器按钮 第12章 不规则按钮大全 实例45 各种不规则按钮 实例46 环形按钮 实例47 三角形按钮 实例48 椭圆形按钮 第13章 掩膜编辑框 实例49 ip地址输入编辑框 实例50 下拉列表控件中的编辑柜 实例51 掩膜输入编辑框 实例52 数字输入控制编辑框 第14章 编辑框和树视控件 实例53 破解密码框内容 实例54 16进制编辑输入框 实例55 树型注释 实例56 目录树结构 第15章 组合框锦 实例57 多列显示的组合框 实例58 自动完成功能的组合框 实例59 浮动的组合框 实例60 浮动的彩色组合框 第16章 操作系统的组合框 实例61 “快照”组合框 实例62 多列属性组合框 实例仍 使用组合框选择颜色 实例64 使用组合框选择字体 第17章 组合框和列表框 实例65 具有历史记录的组合框 实例66 系统图像列表 实例67 excel表格视图 实例68 浏览数据库的列表框 第18章 属性页初级篇 实例69 改变属性页的按钮区 实例70 完整的属性页应用程序 实例71 嵌入对话框中的属性页 实例72 实现wizard 第19章 属性页及标签页高级篇 实例73 在对话框中的属性页 实例74 实现标签下标式的视图切换 实例75 属性列表控件 实例76 标签列表控件 第20章 工具栏锦 实例77 具有下拉按钮的工具栏 实例78 工具栏中的列表框 实例79 各种颜色和大小的工具栏 实例80 气球式提示窗口 第21章 状态栏初级篇 实例81 改变状态栏的状态格 实例82 进度条中的文字 实例83 带声音的提示条 实例84 3d向量控件 第22章 状态栏高级篇 实例85 丰富的状态栏风格 实例86 使用动画控件 实例87 使用日历控件 实例88 使用柱状图控件 第23章 系统操作之一 实例89 方便的日历托盘 实例90 列举系统字体 实例91 实现注册表功能 实例92 检测windows版本号 第24章 系统操作之二 实例93 动画显示的托盘 实例94 浮动窗口 实例95 获得系统硬件信息 第25章 系统操作之三 实例96 修改系统[开始]菜单 实例97 检测系统中的api包 第26章 系统操作之四 实例98 建立自己的任务栏 实例99 隐藏任务条中的应用程序 实例100 检测cpu的时钟 附录a 窗口类与窗口样式 a.1 窗口类结构 a.2 窗口类样式 a.3 窗口样式分组 附录b mfc中windows公共控件的通知消息 b.1 windows公共控件的通知 b.2 通知消息结构 b.3 通知过程概述 b.4 更好地处理通知的方案 b.5 用0n_n0tify_range指定通知域 附录c visual c++开发实用技巧

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值