精华内容
下载资源
问答
  • Visual C++窗体设计技巧集.doc
  • Visual C++窗体设计技巧

    2017-03-20 15:54:48
    主要关于窗体设计的一些心得和经验
  • Visual C++窗体设计技巧集 转:http://blog.csdn.net/Franky_Yu/archive/2005/10/16/505087.aspx [前言:]有好的界面软件就成功了一半,本文将向您介绍怎样设计一些有“稀奇古怪”形状的窗体,如何设定窗体的...

    Visual C++窗体设计技巧集

    转:http://blog.csdn.net/Franky_Yu/archive/2005/10/16/505087.aspx

    [前言:]有好的界面软件就成功了一半,本文将向您介绍怎样设计一些有“稀奇古怪”形状的窗体,如何设定窗体的颜色、如何设置任务栏和状态栏以及菜单图标等等,通过这些技巧能更深入的理解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.rightnFullWidth;
       m_FullScreenRect.bottom=WindowRect.bottom-ClientRect.bottomnFullHeight;
       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);

    }

    }

     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));

    }

     

     

     

     

     

     

     

     

    . 修改主窗口风格 

      AppWizard生成的应用程序框架的主窗口具有缺省的窗口风格,比如在窗口标题条中自动添加文档名、窗口是叠加型的、可改变窗口大小等。要修改窗口的缺省风格,需要重载CWnd::PreCreateWindow(CREATESTRUCT& cs)函数,并在其中修改CREATESTRUCT型参数cs。

    CWnd::PreCreateWindow 函数先于窗口创建函数执行。如果该函数被重载,则窗口创建函数将使用CWnd::PreCreateWindow 函数返回的CREATESTRUCT cs参数所定义的窗口风格来创建窗口;否则使用预定义的窗口风格。

    CREATESTRUCT结构定义了创建函数创建窗口所用的初始参数,其定义如下:

    typedef struct tagCREATESTRUCT {

    LPVOID lpCreateParams; // 创建窗口的基本参数

    HANDLE hInstance; // 拥有将创建的窗口的模块实例句柄

    HMENU hMenu; // 新窗口的菜单句柄

    HWND hwndParent; // 新窗口的父窗口句柄

    int cy; // 新窗口的高度

    int cx; // 新窗口的宽度

    int y; // 新窗口的左上角Y坐标

    int x; // 新窗口的左上角X坐标

    LONG style; // 新窗口的风格

    LPCSTR lpszName; // 新窗口的名称

    LPCSTR lpszClass; // 新窗口的窗口类名

    DWORD dwExStyle; // 新窗口的扩展参数

    } CREATESTRUCT;

    CREATESTRUCT结构的style域定义了窗口的风格。比如,缺省的MDI主窗口的风格中就包括FWS_ADDTOTITLE(在标题条中显示当前的工作文档名)、FWS_PREFIXTITLE(把文档名放在程序标题的前面)、WS_THICKFRAME(窗口具有可缩放的边框)等风格。由于多种风格参数由逻辑或(“|”)组合在一起的,因此添加某种风格,就只需用“|”把对应的参数加到CREATESTRUCT结构的style域中;删除已有的风格,则需用“&”连接CREATESTRUCT结构的style域与该风格的逻辑非值。

    CREATESTRUCT结构的x、y、cx、cy域分别定义了窗口的初始位置和大小,因此,在CWnd::PreCreateWindow 函数中给它们赋值,将能定义窗口的初始显示位置和大小。

    下例中的代码将主框窗口的大小将固定为1/4屏幕,标题条中仅显示窗口名,不显示文档名。

    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

    {

    // TODO: Modify the Window class or styles here by modifying

    // the CREATESTRUCT cs

     

    // 修改主窗风格 

    cs.style &= ~FWS_ADDTOTITLE;去除标题条中的文档名 

    cs.style &= ~WS_THICKFRAME; 去除可改变大小的边框 

    cs.style |= WS_DLGFRAME; 增加不能改变大小的边框 

     

    // 确定主窗的大小和初始位置 

    int cxScreen = ::GetSystemMetrics(SM_CXSCREEN);//获得屏幕宽 

    int cyScreen = ::GetSystemMetrics(SM_CYSCREEN);// 获得屏幕高 

    cs.x = 0; // 主窗位于左上角 

    cs.y = 0;

    cs.cx = cxScreen/2; // 主窗宽为1/2屏幕宽 

    cs.cy = cxScreen/2; // 主窗高为1/2屏幕高 

    return CMDIFrameWnd::PreCreateWindow(cs);

    }

     

     

     

     

     

     

     

    2. 创建不规则形状窗口 

    标准的Windows窗口是矩形的,但在有些时候我们需要非矩形的窗口,比如圆形的、甚至是不规则的。借助CWnd类的SetWindowRgn函数可以创建不规则形状窗口

    CWnd::SetWindowRgn的函数原型如下:

    int SetWindowRgn( HRGN hRgn, // 窗口区域句柄

    BOOL bRedraw ); // 是否重画窗口

    CRgn类封装了关于区域的数据和操作。通过(HRGN)强制操作可以从CRgn类中取得其HRGN值。

    CRgn提供了CreateRectRgn、CreateEllipticRgn和CreatePolygonRgn成员函数,分别用以创建矩形、(椭)圆形和多边形区域。

    创建非矩形窗口的方法如下:首先,在窗口类中定义区域类成员数据(如CRgn m_rgnWnd);其次,窗口的OnCreate函数对话框的OnInitDialog函数中调用CRgn类的CreateRectRgn、CreateEllipticRgn或CreatePolygonRgn函数创建所需的区域,并调用SetWindowRgn函数。

    下例将生成一个椭圆窗口。

    1. 在Developer Studio中选取File菜单中的New命令,在出现的New对话框中选择创建MFC AppWizard(exe)框架应用程序,并输入项目名为EllipseWnd。设定应用程序类型为基于对话框(Dialog based),其它选项按缺省值创建项目源文件。

    2. 使用资源编辑器从主对话框(ID为IDD_ELLIPSEWND_DIALOG)删除其中的所有控制,并从其属性对话框(Dialog Properties)中设定其风格为Popup、无标题条和边框。

    3. 在EllipseWndDlg.h源文件中给主对话框类CEllipseWndDlg增加一个CRgn类保护型数据成员m_rgnWnd,它将定义窗口的区域。

    4. 在EllipseWndDlg.cpp源文件中修改主对话框类CEllipseWndDlg的OnInitDialog()函数,增加m_rgnWnd的创建,并将其定义为窗口区域。粗体语句为新增部分。

    BOOL CEllipseWndDlg::OnInitDialog()

    {

    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.

    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

    ASSERT(IDM_ABOUTBOX < 0xF000);

     

    CMenu* pSysMenu = GetSystemMenu(FALSE);

    if (pSysMenu != NULL)

    {

    CString strAboutMenu;

    strAboutMenu.LoadString(IDS_ABOUTBOX);

    if (!strAboutMenu.IsEmpty())

    {

    pSysMenu->AppendMenu(MF_SEPARATOR);

    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

    }

    }

     

    // Set the icon for this dialog. The framework does this automatically

    // when the application's main window is not a dialog

    SetIcon(m_hIcon, TRUE); // Set big icon

    SetIcon(m_hIcon, FALSE); // Set small icon

     

    // 设置窗口标题为“椭圆窗口”,虽然对话框没有标题条, 

    // 但在任务条的按钮中仍需要标题 

    SetWindowText(_T("椭圆窗口"));

     

    // 取得屏幕宽、高

    int cxScreen = ::GetSystemMetrics(SM_CXSCREEN);

    int cyScreen = ::GetSystemMetrics(SM_CYSCREEN);

    // 设置椭圆X、Y方向的半径

    int nEllipseWidth = cxScreen/8;

    int nEllipseHeight = cyScreen/8;

     

    // 将窗口大小设为宽nEllipseWidth,高nEllipseHeight

    // 并移至左上角

    MoveWindow(0, 0, nEllipseWidth, nEllipseHeight);

    // 创建椭圆区域m_rgnWnd

    m_rgnWnd.CreateEllipticRgn(0, 0, nEllipseWidth, nEllipseHeight);

     

    // 将m_rgnWnd设置为窗口区域

    SetWindowRgn((HRGN)m_rgnWnd, TRUE);

     

    return TRUE; // return TRUE unless you set the focus to a control

    }

     

    3. 用鼠标单击窗口标题条以外区域移动窗口 

    移动标准窗口是通过用鼠标单击窗口标题条来实现的,但对于没有标题条的窗口,就需要用鼠标单击窗口标题条以外区域来移动窗口。有两种方法可以达到这一目标。

    方法一:当窗口确定鼠标位置时,Windows向窗口发送WM_NCHITTEST消息,可以处理该消息,使得只要鼠标在窗口内,Windows便认为鼠标在标题条上。这需要重载CWnd类处理WM_NCHITTEST消息的OnNcHitTest函数,在函数中调用父类的该函数,如果返回HTCLIENT,说明鼠标在窗口客户区内,使重载函数返回HTCAPTION,使Windows误认为鼠标处于标题条上。

    下例是使用该方法的实际代码:

    UINT CEllipseWndDlg::OnNcHitTest(CPoint point)

    {

    // 取得鼠标所在的窗口区域

    UINT nHitTest = CDialog::OnNcHitTest(point);

     

    // 如果鼠标在窗口客户区,则返回标题条代号给Windows

    // 使Windows按鼠标在标题条上类进行处理,即可单击移动窗口

    return (nHitTest==HTCLIENT) ? HTCAPTION : nHitTest;

    }

    方法二:当用户在窗口客户区按下鼠标左键时,使Windows认为鼠标是在标题条上,即在处理WM_LBUTTONDOWN消息的处理函数OnLButtonDown中发送一个wParam参数为HTCAPTIONlParam为当前坐标的WM_NCLBUTTONDOWN消息。 

    下面是使用该方法的实际代码:

    void CEllipseWndDlg::OnLButtonDown(UINT nFlags, CPoint point)

    {

    // 调用父类处理函数完成基本操作

    CDialog::OnLButtonDown(nFlags, point);

     

    // 发送WM_NCLBUTTONDOWN消息

    // 使Windows认为鼠标在标题条上

    PostMessage(WM_NCLBUTTONDOWN,HTCAPTION, MAKELPARAM(point.x, point.y));

    }

     

    4. 使用上下文菜单 

    Windows 95应用程序支持单击鼠标右键弹出上下文菜单的功能,这可通过处理WM_CONTEXTMENU消息来实现。

    当在窗口内单击鼠标右键时,窗口将接收到WM_CONTEXTMENU消息,在该消息的处理函数内装载上下文菜单,并调用CMenu::TrackPopupMenu函数便可显示上下文菜单。CMenu::TrackPopupMenu函数的原型如下:

    BOOL TrackPopupMenu( UINT nFlags, // 显示和选取方式标志

    int x, int y, // 显示菜单的左上角坐标

    CWnd* pWnd, // 接收菜单操作的窗口对象

    LPCRECT lpRect = NULL ); // 敏感区域

    为了使用上下文菜单,首先应在资源编辑器中编制好上下文菜单,假设上下文菜单名为IDR_MENU_CONTEXT;其次,用ClassWizard给窗口增加处理消息WM_CONTEXTMENU的函数OnContextMenu,以及各菜单命令的处理函数;然后编写相应的代码。

    下面的是OnContextMenu函数的代码实例:

    void CEllipseWndDlg::OnContextMenu(CWnd* pWnd, CPoint point)

    {

    CMenu menu;

     

    // 装入菜单 

    menu.LoadMenu(IDR_MENU_CONTEXT);

     

    // 显示菜单

    menu.GetSubMenu(0)->TrackPopupMenu(

    TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON,

    point.x, point.y, this);

    }

     

    5. 使应用程序只能运行一个实例 

    Windows是多进程操作系统,框架生成的应用程序可以多次运行,形成多个运行实例。但在有些情况下为保证应用程序的安全运行,要求程序只能运行一个实例,比如程序要使用只能被一个进程单独使用的特殊硬件(例如调制解调器)时,必须限制程序只运行一个实例。

    这里涉及两个基本的问题,一是在程序的第二个实例启动时,如何发现该程序已有一个实例在运行,二是如何将第一个实例激活,而第二个实例退出。 

    对于第一个问题,可以通过给应用程序设置信号量,实例启动时首先检测该信号量,如已存在,则说明程序已运行一个实例。

    第二个问题的难点是获取第一个实例的主窗对象指针或句柄,然后便可用SetForegroundWindow来激活。虽然FindWindow函数能寻找正运行着的窗口,但该函数要求指明所寻找窗口的标题或窗口类名,不是实现通用方法的途径。我们可以用Win 32 SDK函数SetProp来给应用程序主窗设置一个特有的标记。用GetDesktopWindow可以获取Windows系统主控窗口对象指针或句柄,所有应用程序主窗都可看成该窗口的子窗口,即可用GetWindow函数来获得它们的对象指针或句柄。用Win 32 SDK函数GetProp查找每一应用程序主窗是否包含有我们设置的特定标记便可确定它是否我们要寻找的第一个实例主窗。使第二个实例退出很简单,只要让其应用程序对象的InitInstance函数返回FALSE即可。此外,当主窗口退出时,应用RemoveProp函数删除我们为其设置的标记。

    下面的InitInstance、OnCreate和OnDestroy函数代码将实现上述的操作:

    BOOL CEllipseWndApp::InitInstance()

    {

    // 用应用程序名创建信号量

    HANDLE hSem = CreateSemaphore(NULL, 1, 1, m_pszExeName);

     

    // 信号量已存在?

    // 信号量存在,则程序已有一个实例运行

    if (GetLastError() == ERROR_ALREADY_EXISTS)

    {

    // 关闭信号量句柄

    CloseHandle(hSem);

     

    // 寻找先前实例的主窗口

    HWND hWndPrevious = ::GetWindow(::GetDesktopWindow(),

    GW_CHILD);

    while (::IsWindow(hWndPrevious))

    {

    // 检查窗口是否有预设的标记?

    // 有,则是我们寻找的主窗

    if (::GetProp(hWndPrevious, m_pszExeName))

    {

    // 主窗口已最小化,则恢复其大小

    if (::IsIconic(hWndPrevious))

    ::ShowWindow(hWndPrevious,

    SW_RESTORE);

    // 将主窗激活

    ::SetForegroundWindow(hWndPrevious);

    // 将主窗的对话框激活

    ::SetForegroundWindow(

    ::GetLastActivePopup(hWndPrevious));

     

    // 退出本实例

    return FALSE;

    }

     

    // 继续寻找下一个窗口

    hWndPrevious = ::GetWindow(hWndPrevious,

    GW_HWNDNEXT);

    }

     

    // 前一实例已存在,但找不到其主窗

    // 可能出错了

    // 退出本实例

    return FALSE;

    }

     

    AfxEnableControlContainer();

     

    // Standard initialization

    // If you are not using these features and wish to reduce the size

    // of your final executable, you should remove from the following

    // the specific initialization routines you do not need.

     

    #ifdef _AFXDLL

    Enable3dControls(); // Call this when using MFC in a shared DLL

    #else

    Enable3dControlsStatic();// Call this when linking to MFC statically

    #endif

     

    CEllipseWndDlg dlg;

    m_pMainWnd = &dlg;

    int nResponse = dlg.DoModal();

    if (nResponse == IDOK)

    {

    // TODO: Place code here to handle when the dialog is

    // dismissed with OK

    }

    else if (nResponse == IDCANCEL)

    {

    // TODO: Place code here to handle when the dialog is

    // dismissed with Cancel

    }

     

    // Since the dialog has been closed, return FALSE so that we exit the

    // application, rather than start the application's message pump.

    return FALSE;

    }

     

    int CEllipseWndDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)

    {

    if (CDialog::OnCreate(lpCreateStruct) == -1)

    return -1;

     

    // 设置寻找标记

    ::SetProp(m_hWnd, AfxGetApp()->m_pszExeName, (HANDLE)1);

     

    return 0;

    }

     

    void CEllipseWndDlg::OnDestroy()

    {

    CDialog::OnDestroy();

     

    // 删除寻找标记

    ::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);

    }

     

    6. 使应用程序显示为任务条通知区中的图标 

    在Windows 95任务条的右边有一个区域被称为通知区域,在其中可以显示一些应用程序的图标,用鼠标单击其中的图标一般能弹出应用程序的菜单,双击则能显示应用程序的完整窗口界面。时钟和音量控制是任务条通知区最常见的图标。

    任务条通知区编程可以通过Windows 95外壳编程接口函数Shell_NotifyIcon来实现,该函数在shellapi.h头文件中声明,其原型如下:

    WINSHELLAPI BOOL WINAPI Shell_NotifyIcon( DWORD dwMessage,

    PNOTIFYICONDATA pnid);

    dwMessage是对通知区图标进行操作的消息,主要有三中,如下表所示。

    Shell_NotifyIcon使用的消息

    消息

    说明

    NIM_ADD

    在任务条通知区插入一个图标

    NIM_ DELETE

    在任务条通知区删除一个图标

    NIM_ MODIFY

    对任务条通知区的图标进行修改

     

    pnid传入一个NOTIFYICONDATA结构的指针。NOTIFYICONDATA结构声明及各域的意义表示如下:

    typedef struct _NOTIFYICONDATA { // nid

    DWORD cbSize; // NOTIFYICONDATA结构的字节数

    HWND hWnd; // 处理通知区图标消息的窗口句柄

    UINT uID; // 通知区图标的ID

    UINT uFlags; // 表示下述三项是否有意义的标志

    UINT uCallbackMessage; // 鼠标点击图标所发出消息的ID

    HICON hIcon; // 图标句柄

    char szTip[64]; // 当鼠标移到图标上时显示的提示信息

    } NOTIFYICONDATA, *PNOTIFYICONDATA;

    当用Shell_NotifyIcon在任务条通知区中放置一个图标时,同时也定义了一条回调消息,当用户用鼠标单击或双击图标时,NOTIFYICONDATA结构中指定的窗口句柄将接受到该消息。该消息的lParam参数将说明鼠标操作的方式。当应用程序退出时,应删除任务条中的图标。

     

    下面的示例将说明如何使前述的椭圆窗口程序作为图标显示在任务条通知区中,当鼠标单击图标时,将弹出一个菜单,当双击时,椭圆窗口将完整显示。

    1. 用资源编辑器在EllipseWnd项目的IDR_MENU_CONTEXT菜单中增加一个菜单项“在任务条中插入图标”(ID为IDM_INSERTICON)。

    2. 用资源编辑器在EllipseWnd项目中增加一个菜单资源IDR_MENU_ICON ,在其中设定三个菜单项:

    “激活椭圆窗口”(ID为IDM_ACTIVEWINDOW)

    “关于...”(ID为IDM_ABOUTBOX)

    “退出 Alt+F4”(ID为IDM_EXIT)

    3. 在CEllipseWndDlg.h源文件中定义一个消息UM_ICONNOTIFY用以响应图标操作,并在CEllipseWndDlg类定义中增加响应该消息的处理函数OnIconNotify。用ClassWizard增加响应菜单命令IDM_INSERTICON和IDM_ACTIVEWINDOW的函数定义和模板。CEllipseWndDlg.h中的修改如下:

     

    // 定义响应图标操作的消息

    #define UM_ICONNOTIFY WM_USER+100

     

    class CEllipseWndDlg : public CDialog

    {

    // Construction

    public:

    CEllipseWndDlg(CWnd* pParent = NULL); // standard constructor

     

    // Dialog Data

    file://{{AFX_DATA(CEllipseWndDlg)

    enum { IDD = IDD_ELLIPSEWND_DIALOG };

    // NOTE: the ClassWizard will add data members here

    file://}}AFX_DATA

     

    // ClassWizard generated virtual function overrides

    file://{{AFX_VIRTUAL(CEllipseWndDlg)

    protected:

    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

    file://}}AFX_VIRTUAL

     

    // Implementation

    protected:

    HICON m_hIcon;

    CRgn m_rgnWnd;

     

    // 处理图标的功能函数说明

    BOOL AddIcon();

    BOOL DeleteIcon();

     

    // Generated message map functions

    file://{{AFX_MSG(CEllipseWndDlg)

    virtual BOOL OnInitDialog();

    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

    afx_msg void OnPaint();

    afx_msg HCURSOR OnQueryDragIcon();

    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

    afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);

    afx_msg void OnAboutbox();

    afx_msg void OnExit();

    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

    afx_msg void OnDestroy();

    afx_msg void OnInserticon();

    afx_msg void OnActivewindow();

    file://}}AFX_MSG

    // 图标消息的处理函数说明

    afx_msg void OnIconNotify(WPARAM wParam, LPARAM lParam);

    DECLARE_MESSAGE_MAP()

    };

     

    4. 在CEllipseWndDlg.cpp中增加消息影射条目如下:

    BEGIN_MESSAGE_MAP(CEllipseWndDlg, CDialog)

    file://{{AFX_MSG_MAP(CEllipseWndDlg)

    ON_WM_SYSCOMMAND()

    ON_WM_PAINT()

    ON_WM_QUERYDRAGICON()

    ON_WM_LBUTTONDOWN()

    ON_WM_CONTEXTMENU()

    ON_COMMAND(IDM_ABOUTBOX, OnAboutbox)

    ON_COMMAND(IDM_EXIT, OnExit)

    ON_WM_CREATE()

    ON_WM_DESTROY()

    ON_COMMAND(IDM_INSERTICON, OnInserticon)

    ON_COMMAND(IDM_ACTIVEWINDOW, OnActivewindow)

    file://}}AFX_MSG_MAP

    ON_MESSAGE(UM_ICONNOTIFY, OnIconNotify)

    END_MESSAGE_MAP()

     

    5. 在CEllipseWndDlg.cpp中增加如下的函数或代码:

    void CEllipseWndDlg::OnDestroy()

    {

    CDialog::OnDestroy();

     

    // remove main window tag

    ::RemoveProp(m_hWnd, AfxGetApp()->m_pszExeName);

     

    // 应用程序退出时,删除任务条中图标

    DeleteIcon();

    }

     

    BOOL CEllipseWndDlg::AddIcon()

    {

    // 在任务条中增加图标

    NOTIFYICONDATA nid;

    nid.cbSize = sizeof(nid);

    nid.hWnd = m_hWnd;

    nid.uID = IDR_MAINFRAME;

    nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;

    nid.uCallbackMessage = UM_ICONNOTIFY;

    nid.hIcon = m_hIcon;

    CString str = "椭圆窗口";

    lstrcpyn(nid.szTip, (LPCSTR)str,

    sizeof(nid.szTip) / sizeof(nid.szTip[0]));

     

    return Shell_NotifyIcon(NIM_ADD, &nid);

    }

     

    BOOL CEllipseWndDlg::DeleteIcon()

    {

    // 删除任务条中的图标

    NOTIFYICONDATA nid;

    nid.cbSize = sizeof(nid);

    nid.hWnd = m_hWnd;

    nid.uID = IDR_MAINFRAME;

     

    return Shell_NotifyIcon(NIM_DELETE, &nid);

    }

     

    // 响应图标消息处理函数

    void CEllipseWndDlg::OnIconNotify(WPARAM wParam,

    LPARAM lParam)

    {

    switch ((UINT)lParam)

    {

    // 鼠标单击操作

    case WM_LBUTTONDOWN:

    case WM_RBUTTONDOWN:

    {

    // 装入图标操作菜单

    CMenu menu;

    menu.LoadMenu(IDR_MENU_ICON);

     

    // 鼠标单击位置

    CPoint point;

    GetCursorPos(&point);

     

    // 将背景窗口激活

    SetForegroundWindow();

     

    // 显示图标菜单

    menu.GetSubMenu(0)->TrackPopupMenu(

    TPM_LEFTBUTTON|TPM_RIGHTBUTTON,

    point.x, point.y, this, NULL);

     

    // 增加一个额外消息,使菜单操作正确

    PostMessage(WM_USER, 0, 0);

    break;

    }

    // 鼠标双击操作

    case WM_LBUTTONDBLCLK:

    // 激活应用程序

    OnActivewindow();

    break;

    }

    }

     

    // 插入图标到任务条通知区

    void CEllipseWndDlg::OnInserticon()

    {

    // 先隐藏主窗

    ShowWindow(SW_HIDE);

    // 插入图标

    AddIcon();

    }

     

    // 激活主窗

    void CEllipseWndDlg::OnActivewindow()

    {

    // 先删除图标

    DeleteIcon();

    // 显示主窗

    ShowWindow(SW_SHOW);

    UpdateWindow();

    }

     

    7. 显示旋转文本 

    在有的应用中,为了达到特殊的效果,经常需要显示旋转的文本。文本的显示方式,包括旋转,都是由字体来设置的。 

    字体的属性主要由创建字体时使用的LOGFONT结构规定,该结构中的lfEscapement指定了文本行与X轴(水平轴)的角度,其角度单位是十分之一度。为了使所有的字体向相同的方向旋转,还应同时将LOGFONT结构的lfClipPrecision域设为 CLIP_LH_ANGLES

    下面的代码将在对话框中显示在同一起点每隔15度显示一行文本:

    void CRotateTextDlg::OnPaint()

    {

    CPaintDC dc(this); // device context for painting

     

    if (IsIconic())

    {

    SendMessage(WM_ICONERASEBKGND,  (WPARAM) dc.GetSafeHdc(),  0);

     

    // Center icon in client rectangle

    int cxIcon = GetSystemMetrics(SM_CXICON);

    int cyIcon = GetSystemMetrics(SM_CYICON);

    CRect rect;

    GetClientRect(&rect);

    int x = (rect.Width() - cxIcon + 1) / 2;

    int y = (rect.Height() - cyIcon + 1) / 2;

     

    // Draw the icon

    dc.DrawIcon(x, y, m_hIcon);

    }

    else

    {

    CRect rc;

    GetClientRect(rc);

     

    CString str(_T("............旋转文本!"));

     

    dc.SetBkMode(TRANSPARENT);

    dc.SetTextColor(RGB(0,0,255));

     

    CFont font;

    LOGFONT lf;

    memset(&lf,0,sizeof(LOGFONT));

     

    lf.lfHeight = -14;

    lf.lfWeight = FW_NORMAL;

    lf.lfClipPrecision = CLIP_LH_ANGLES;

    strcpy(lf.lfFaceName, "宋体");

     

    for (int i=0;i<3600;i+=150)

    {

    lf.lfEscapement = i;

     

    font.CreateFontIndirect(&lf);

    CFont *pOldFont = dc.SelectObject(&font);

     

    dc.TextOut(rc.right/2, rc.bottom/2,str);

     

    dc.SelectObject(pOldFont);

    font.DeleteObject();

    }

     

    CDialog::OnPaint();

    }

    }

    转载于:https://www.cnblogs.com/lanru/archive/2010/11/15/1877626.html

    展开全文
  • Visual C++窗体设计技巧集 (来源:yesky ) [前言:]有好的界面软件就成功了一半,本文将向您介绍怎样设计一些有“稀奇古怪”形状的窗体,如何设定窗体的颜
    Visual C++窗体设计技巧集 (来源:yesky )
          [前言:]有好的界面软件就成功了一半,本文将向您介绍怎样设计一些有“稀奇古怪”形状的窗体,如何设定窗体的颜色、如何设置任务栏和状态栏以及菜单图标等等,通过这些技巧能更深入的理解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);
           }
         }

      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 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);
    }
    展开全文
  • [转帖] Visual C++窗体设计技巧集

    千次阅读 2005-06-29 10:19:00
    http://blog.csdn.net/simb/archive/2005/06/28/406261.aspx  [前言:]有好的界面软件就成功了一半,本文将向您介绍怎样设计一些有“稀奇古怪”形状的窗体,如何设定窗体的颜色、如何设置任务栏和状态栏以及菜单...

    http://blog.csdn.net/simb/archive/2005/06/28/406261.aspx


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

      如何制作透明窗体

      使用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
    }

      使用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);

    }

    }

      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 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);
    }

    展开全文
  • c++窗体与界面设计

    2017-01-03 21:23:23
    多媒体触摸屏程序应用实例
  • 在初步学习了c++之后,我们可以着手设计一个窗口类程序,但在这之前,我们得知道c++里面是怎么设计窗口的。 开始创建窗口之前,我们需要包含几个关键的头文件: #include <windows.h> #include <stdlib.h&...

    (本文使用VS2017)
    在初步学习了c++之后,我们可以着手设计一个窗口类程序,但在这之前,我们得知道c++里面是怎么设计窗口的。
    开始创建窗口之前,我们需要包含几个关键的头文件:

    #include <windows.h>
    #include <stdlib.h>
    #include <tchar.h>
    

    当然,如果你和我一样,是这么创建的(如下图),那么VS2017会自动包含一个stdafx.h的预编译头文件,里面包含了很多我们可能要用到的头文件,也就不用再重复输入上面的头文件。创建窗体程序项目窗口

    #include "stdafx.h"
    

    另外通过VS2017创建的桌面应用程序,会自动包含一个窗口创建模板,该模板甚至能直接运行,非常有利于我们的学习。
    在这里插入图片描述
    我们先跳过长长的函数和变量名,直接跳到注册窗口类。
    在这里插入图片描述
    众所周知,窗口,就是打开我的电脑、任意文档、甚至浏览器所弹出的界面。这个界面是可以变化的(长宽、风格、名称等等)。
    在c++中也一样,在创建了一个窗口后,你可以为该窗口配置属性。
    c++自带了一个类 WNDCLASS( 一般用WNDCLASSEXW,EX表示增强版,W表示宽字符),用于创建窗口对象, WNDCLASSEXW中有很多类成员对象,用于配置窗口属性。

    看函数MyRegisterClass内的语句:
    看不懂的地方可以先跳过,照着写就行

     WNDCLASSEXW wcex; //WNDCLASSEXW是窗口类,此句的意思是创建一个名为wcex的窗口对象,也就是我们要创建的窗口
    
        wcex.cbSize = sizeof(WNDCLASSEX);//wcex窗口的大小,一般都用sizeof获取标准大小,我也不知道为啥
    
        wcex.style          = CS_HREDRAW | CS_VREDRAW;//窗口样式,详见下文
        wcex.lpfnWndProc    = WndProc;				//窗口回调函数,用来记录你在窗口进行的操作,并返回操作导致的结果,后面再细讲。
        wcex.cbClsExtra     = 0;					//窗口类的额外空间,单位:字节数。这地方和下面都比较难理解,这是正常的,不慌。
        wcex.cbWndExtra     = 0;					//窗口实例的额外空间,单位:字节数。
        wcex.hInstance      = hInstance;			//窗口实例句柄,
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT4));			//窗口图标句柄
        wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);//鼠标光标句柄
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);//窗口背景颜色句柄,该句表示默认颜色(实际上也可以把+1换成+2/3改颜色)
        wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT4);//所用菜单名称,资源文件中有一个后缀rc的文件记录了菜单方案
        wcex.lpszClassName  = szWindowClass;		//窗口类的名称
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//窗口的小图标句柄
    

    下面对一些重要的对象成员进行解释:
    ①style 窗口样式
    CS_HREDRAW | CS_VREDRAW意味着当窗口宽度或高度发生变化时,窗口将根据窗口大小重新绘制。其中前缀CS表示class type,HREDRAW代表当宽度(H代表水平方向Horizontal)发生改变时进行重绘(Redraw),VREDRAW代表着当高度(V代表垂直方向Vertical)方向发生变化时进行重绘。
    除此之外,style还可以设置其他的值,常见的值如下:

    CS_BYTEALIGNCLIENT
    窗口的客户区域以“字符边界”对齐,当系统调整窗口的水平位置时,客户区域的左边坐标是8的整数倍

    CS_BYTEALIGNWINDOW
    窗口以“字符边界”对齐,当系统调整窗口的水平位置时,客户区域的左边坐标是8的整数倍

    CS_CLASSDC
    分配一个设备环境并被类中的所有窗体共享。它是可以适用于一个应用程序的若干线程创建的一个相同类的窗体。当多个线程试图同时使用相同的设备环境时,系统只允许一个线程成功地进行绘图操作

    CS_DBLCLKS
    当用户双击窗口时,将向窗口函数发送鼠标双击消息

    CS_GLOBALCLASS
    指定此窗体类是一个应用程序全局类。应用程序全局类是由一个在进程中对所有模块有效的exe或dll注册的窗体类

    CS_HREDRAW
    如果窗口的位置或宽度发生改变,将重绘窗口

    CS_NOCLOSE
    窗口中的“关闭”按钮不可见

    CS_OWNDC
    为同一个窗口类中的每个窗口创建一个唯一的设备上下文

    CS_PARENTDC
    设置子窗口中剪下的矩形区域到父窗口中,以使子窗口可以在父窗口上绘图。指定该风格可以提高应用程序的性能

    CS_SAVEBITS
    把被窗口遮掩的屏幕图像作为位图保存起来。当该窗口被移动时,Windows操作系统使用被保存的位图来重建屏幕图像

    CS_VREDRAW
    如果窗口的位置或高度改变,将重绘窗口

    ②lpfnWndProc 窗口回调函数
    这个函数用于处理用户对窗口的操作,此处可以右键WndProc转到定义,查看该函数的定义。
    WndProc包含四个参数:hwnd是要处理窗口的句柄;message是消息ID,代表了不同的消息类型;wParam的值为按下按键的虚拟键码;lParam则存储按键的相关状态信息。
    这个函数留到后面再讲。

    ③cbClsExtra/cbWndExtra
    说实话,这个地方我也没太理解,但我找到一个人讲的比较好,看这个链接:
    https://blog.csdn.net/zlk1214/article/details/50736674

    ④hInstance 窗口实例句柄
    句柄可以理解为一个编号,即窗口实例的编号。因为在实际应用中,我们会打开多个窗口,Windows为了区别不同的窗口,所以要给每个窗口设置一个不同的句柄。

    ⑤hIcon 窗口图标句柄
    hIcon和hIconSm都是表示窗口图标的成员,不同的是,hIcon表示任务栏的图标,hIconSm表示小图标,如图所示:
    在这里插入图片描述
    那么如何更改图标呢?
    首先我们需要一个后缀为ico的标准图标文件,我们可以在很多地方下载到。
    比如https://www.easyicon.net/
    在这里插入图片描述
    下载完之后得到一个ico文件,我们把它移动到你新建的项目中(如果不知道路径,可以在资源视图中看到)
    在这里插入图片描述
    如图所示,这样在资源视图icon目录下会多出一个个IDI_ICON1的文件(当然你也可以叫其他名字)
    我们再切换到Resource.h头文件中,给我们导入的IDI_CON1一个编号:
    在这里插入图片描述

    这样我们就能在WNDCLASSEX对象中使用它了,使:

    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
     wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON1));//注意到哪里替换了吗?
    

    再运行就是我们想要的图标了:
    在这里插入图片描述

    ⑥hCursor 光标句柄
    改变鼠标光标的语句,目前我也不太会用,估计和改变图标差不多,先就默认吧。

    ⑦hbrBackground 背景颜色
    (HBRUSH)(COLOR_WINDOW+1),表示windows默认的颜色,+1没有特别的含义,因为滚动条颜色宏被定义成 #define COLOR_SCROLLBAR 0如果不进行+1的话,使用这个颜色就是意外的变成NULL。
    我们也可以使用CreateSolidBrush(RGB(255,100,100)),来调制颜色,RBG里面的数字自己调。
    如图:
    在这里插入图片描述
    ⑧lpszMenuName 菜单名
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT4);
    我们可以在右侧的资源文件中看到菜单设计(IDC_WINDOWSPROJECT4)
    在这里插入图片描述
    更方便的自定义菜单方案还是在资源视图中,你一试便知。
    在这里插入图片描述
    ⑨lpszClassName 窗口类的名字
    以上过程实际上只是在电脑中创建了一个对象,并未实际创建窗口,如果要把我们创建的窗口对象显示出来,需要用到CreateWindowW语句(你可以在后面的语句中找到它),当创建窗口时,就需要用到窗口类的名字。(这一段我纯属瞎扯,等我以后整明白了再改。)

    至此,我们已经大致理清了窗体类WNDCLASSEX中各成员参数的含义,但还有更多的问题等待着我们搞明白,因此,未完待续……

    展开全文
  • C++写的窗体设计

    2012-10-21 21:56:52
    我自己用C++开发了一个窗体设计器,类似于Delphi的窗体设计工具,目前还不是很完善,不过基本功能是有的
  • 我实现了一个多窗体类上面有很多控件数据,同时根据要求我要实现多种类似的窗体,这些小窗体显示在一个大的窗体中,每个小窗体的框架一样,但是数据内容不一样,当时我是用窗体动态创建实现的,但是人家不让,说是让...
  • 【摘要】本文以C++菜菜鸟(仅仅须要学习了C++数据类型和控制结构就可以)为目标读者,用求解一元二次方程作为实例,展示窗体式程序的开发过程,获得初步体验。写作目的包含:(1)让学生通过模仿,开发出类似风格的...
  • C++Builder开发第2章__窗体设计与常用控件
  • 网上商城 各种功能C++窗体大作业毕业设计首选 使用C++/CLR语言并基于公共语言运行库来编写“天猫商城”的本地客户端软件,完成普通用户的注册、充值、购买商品;管理员对商城进行管理等功能的实现。
  • 【摘要】本文适合已经完整学习了C++面向对象机制,但在开发窗体程序方面还是零基础的同学。通过本文的引导进行实践体验,目的是消除同学们开发窗体程序的神奇感...【相关博文】C++窗体”程序设计启蒙(之中的一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 549
精华内容 219
关键字:

c++窗体设计

c++ 订阅