• 自绘窗口边框和标题栏


    自绘窗口需要响应如下的几个消息:

    1、WM_NCCALCSIZE
    这个是用来返回NC区域的。windows系统根据这个消息的返回,决定矩形区域中,NC区域在哪里。如下的代码是一个实现:
    void CSIPanel::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)
    {
       RECT r;
       // TODO: Add your message handler code here and/or call default
       if (m_psi)
       {
          if (m_psi->m_bCustomWindow)
          {
              memcpy(&r,&lpncsp->rgrc[0],sizeof(RECT));
              r.top = r.top+m_psi->m_NcRect.top;
              r.bottom = r.bottom - m_psi->m_NcRect.bottom;
              r.left = r.left+m_psi->m_NcRect.left;
              r.right = r.right-m_psi->m_NcRect.right;
              memcpy(&lpncsp->rgrc[0],&r,sizeof(RECT));
              return;
           }
       }
       CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
    }
    在这个代码中,m_psi->m_bCustomWindow是用来标志是否是自绘窗口的。如果不是自绘窗口,才需要调整大小。至于bCalcValidRects这个参数,说老实话我也不知道它的用处。我查过很多的代码,都没有使用这个参数(比如BCGPRO的代码等),而实际情况下也是不使用也没有问题的。完全只需要更改lpncsp->rgrc[0]就可以了。

    2、需要响应NCPAINT消息
    (如果在窗口非active的情况下,并不绘制ncpaint或者调用原来的ncpaint,那么系统就会画出以前的难看的窗口边框和标题)很简单,代码:
    void CSIPanel::OnNcPaint()
    {
       CWindowDC dc(this);
       if (m_psi == NULL)
          return;
       if (m_psi->m_bCustomWindow == FALSE)
          return;
       p_PaintCorners(&dc);
       p_PaintIcons(&dc);
       p_PaintPushedIcons(&dc);
       p_PaintTitleLine(&dc);
    }

    3、还需要响应CREATE消息
    为何响应Create消息?因为窗口是不规则的(不是直角矩形而是园角矩形的)
    int CSIPanel::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
       if (CWnd::OnCreate(lpCreateStruct) == -1)
          return -1;
       // TODO: Add your specialized creation code here
       ResetWindowRgn();
       SetWindowPos(NULL,0,0,0,0,SWP_FRAMECHANGED|SWP_NOOWNERZORDER|
             SWP_NOMOVE|SWP_NOSIZE);
       return 0;
    }
    SetWindowPos是强制让Window重新计算大小(NCCALCSIZE)和绘制。
    /////////////////////////////////////////////////////////////////////////////
    // CSIPanel message handlers
    void CSIPanel::ResetWindowRgn()
    {
       int iRet;
       BOOL bRet;
       CRgn m_rgn1,m_rgn2,m_rgn3;
       RECT r;
       GetWindowRect(&r);
       OffsetRect(&r,-r.left,-r.top);
       bRet = m_rgn1.CreateRoundRectRgn(r.left,r.top,r.right+1,r.top+45,12,12);
       m_rgn2.CreateRoundRectRgn(r.left,r.top+18,r.right+1,r.bottom+2,12,12);
       iRet = m_rgn2.CombineRgn(&m_rgn1,&m_rgn2,RGN_OR);
       iRet = SetWindowRgn(m_rgn2,TRUE);
    }

    4、还需要响应WM_SIZE
    void CSIPanel::OnSize(UINT nType, int cx, int cy)
    {
       CWnd::OnSize(nType, cx, cy);
       // TODO: Add your message handler code here
       ResetWindowRgn();
       Invalidate();
    }

    5、响应ACTIVE
    用于绘制不同情况下的标题
    void CSIPanel::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
    {
       CWnd::OnActivate(nState, pWndOther, bMinimized);
       if (nState == WA_INACTIVE)
       {
          if (m_bTraceFlag)
          {
             ReleaseCapture();
             m_bTraceFlag = FALSE;
          }
          m_bActive = FALSE;
       }
       else
          m_bActive = TRUE;
       SetWindowPos(NULL,0,0,0,0,SWP_FRAMECHANGED|SWP_NOOWNERZORDER|
            SWP_NOMOVE|SWP_NOSIZE);
    }
    这里为何要检测一个鼠标的情况呢?因为如果在标题行上有自绘的小按钮,那么,我们需要跟踪鼠标才行(响应NCMOUSELCLICK是不行的)所以,如果本窗口失去焦点,应该RelaseCapture。

    6、响应NCLBUTTONDOWN
    为了能够在标题行绘制自己的最大最小按钮,并且能够响应它,就绪要响应在标题行上的鼠标左键。但是为了能够像window操作一样,鼠标左键按下的时候不触发,而是鼠标左键抬起的时候触发,这就需要在NCLBUTTONDOWN的时候做SetCapture.
    void CSIPanel::OnNcLButtonDown(UINT nHitTest, CPoint point)
    {
       // TODO: Add your message handler code here and/or call default
       RECT r;
       RECT r1;
       BOOL bRet;
       int iSizeX,iSizeY;

       if (m_bTraceFlag == TRUE)
          return;
       m_bMinPushed = FALSE;
       m_bMaxPushed = FALSE;
       m_bClosePushed = FALSE;
       m_bSysPushed = FALSE;
       GetWindowRect(&r);
       if (m_bActive == FALSE)
          goto L_DEFAULT;
       r.bottom = r.top + (m_psi->m_rLT.bottom - m_psi->m_rLT.top);
       bRet = PtInRect(&r,point);
       if (bRet == FALSE)
          goto L_DEFAULT;
       /* ----------------------------------------------------------
       * 判定是否在按钮上按下的.
       * ---------------------------------------------------------- */
       if (m_bIconSys)
       {
          r1.left = r.left + 5;
          r1.top = r.top + 5;
          r1.right = m_psi->m_rSYS.right - m_psi->m_rSYS.left + r1.left;
          r1.bottom = m_psi->m_rSYS.bottom- m_psi->m_rSYS.top + r1.top;
          bRet = PtInRect(&r1,point);
          if (bRet == TRUE)
         {
             m_bSysPushed = TRUE;
             goto L_FOUND;
          }
       }
       if (m_bIconMin)
       {
          iSizeX = m_psi->m_rCLOSE.right - m_psi->m_rCLOSE.left;
          iSizeY = m_psi->m_rCLOSE.bottom - m_psi->m_rCLOSE.top;
          r1.left = r.right - 6 - 3 * iSizeX - 3 * 2;
          r1.top = r.top+5;
          r1.right = r1.left + iSizeX;
          r1.bottom = r1.top + iSizeY;
          bRet = PtInRect(&r1,point);
          if (bRet == TRUE)
          {
             m_bMinPushed= TRUE;
             goto L_FOUND;
          }
       }
       if (m_bIconMax)
       {
          iSizeX = m_psi->m_rCLOSE.right - m_psi->m_rCLOSE.left;
          iSizeY = m_psi->m_rCLOSE.bottom - m_psi->m_rCLOSE.top;
          r1.left = r.right - 6 - 2 * iSizeX - 2 * 2;
          r1.top = r.top+5;
          r1.right = r1.left + iSizeX;
          r1.bottom = r1.top + iSizeY;
          bRet = PtInRect(&r1,point);
          if (bRet == TRUE)
          {
             m_bMaxPushed= TRUE;
             goto L_FOUND;
          }
       }
       if (m_bIconClose)
       {
          iSizeX = m_psi->m_rCLOSE.right - m_psi->m_rCLOSE.left;
          iSizeY = m_psi->m_rCLOSE.bottom - m_psi->m_rCLOSE.top;
          r1.left = r.right - 6 - iSizeX - 2;
          r1.top = r.top+5;
          r1.right = r1.left + iSizeX;
          r1.bottom = r1.top + iSizeY;
          bRet = PtInRect(&r1,point);
          if (bRet == TRUE)
          {
             m_bClosePushed= TRUE;
             goto L_FOUND;
          }
       }
    L_DEFAULT:
       CWnd::OnNcLButtonDown(nHitTest, point);
       return;
    L_FOUND:
       m_bTraceFlag = TRUE;
       SetCapture();
       RepaintIcons();
       return;
    }

    7、当然要响应LBUTTONUP消息
    为何是LBUTTONUP而不是NCLBUTTONUP?因为在NCLBUTTONDOWN的时候SetCapture之后,就是LBUTTONUP消息了。(应该不会有NCLBUTTONUP消息的)。
    void CSIPanel::OnLButtonUp(UINT nFlags, CPoint point)
    {
       // TODO: Add your message handler code here and/or call default
       RECT r;
       CWnd::OnLButtonUp(nFlags, point);
       if (m_bTraceFlag == FALSE)
          return;
       ReleaseCapture();
       GetWindowRect(&r);
       m_bTraceFlag = FALSE;
       if (m_bSysPushed == TRUE)
       {
          // PostMessage(WM_SYSCOMMAND,SC_MOUSEMENU,r.left<<16|r.top);
         PostMessage(WM_SYSCOMMAND,SC_KEYMENU,r.left<<16|r.top);
       }
       else if (m_bMinPushed == TRUE)
       {
          PostMessage(WM_SYSCOMMAND,SC_MINIMIZE,r.left<<16|r.top);
       }
       else if (m_bMaxPushed == TRUE)
       {
          PostMessage(WM_SYSCOMMAND,SC_MAXIMIZE,r.left<<16|r.top);
       }
       else if (m_bClosePushed == TRUE)
       {
          PostMessage(WM_SYSCOMMAND,SC_CLOSE,r.left<<16|r.top);
       }
       m_bMinPushed = FALSE;
       m_bMaxPushed = FALSE;
       m_bClosePushed = FALSE;
       m_bSysPushed = FALSE;
    }

    8、剩下的就是响应MOUSEMOVE
    为何响应MOUSEMOVE?因为我们注意到,普通windows按钮的性格是按下的时候BUTTON PUSHED,保持鼠标左键按下,移开鼠标的时候BUTTON UP,然后再到这个BUTTON上抬起鼠标左键才触发,为了能做的差不多像,我们有:
    void CSIPanel::OnMouseMove(UINT nFlags, CPoint px)
    {
    // TODO: Add your message handler code here and/or call default
    RECT r,r1,rc;
    BOOL bRet;
    int iSizeX,iSizeY;
    POINT point;
    CWnd::OnMouseMove(nFlags, px);
    if (m_bTraceFlag == FALSE)
    return;
    GetWindowRect(&r);
    GetClientRect(&rc);
    ClientToScreen(&rc);
    point.x = rc.left + px.x;
    point.y = rc.top + px.y;
    /* ----------------------------------------------------------
    * 判定是否在按钮上按下的.
    * ---------------------------------------------------------- */
    if (m_bIconSys)
    {
    r1.left = r.left + 5;
    r1.top = r.top + 5;
    r1.right = m_psi->m_rSYS.right - m_psi->m_rSYS.left + r1.left;
    r1.bottom = m_psi->m_rSYS.bottom- m_psi->m_rSYS.top + r1.top;
    bRet = PtInRect(&r1,point);
    if (bRet == TRUE)
    {
    m_bSysPushed = TRUE;
    goto L_FOUND;
    }
    }
    if (m_bIconMin)
    {
    iSizeX = m_psi->m_rCLOSE.right - m_psi->m_rCLOSE.left;
    iSizeY = m_psi->m_rCLOSE.bottom - m_psi->m_rCLOSE.top;
    r1.left = r.right - 6 - 3 * iSizeX - 3 * 2;
    r1.top = r.top+5;
    r1.right = r1.left + iSizeX;
    r1.bottom = r1.top + iSizeY;
    bRet = PtInRect(&r1,point);
    if (bRet == TRUE)
    {
    m_bMinPushed= TRUE;
    goto L_FOUND;
    }
    }
    if (m_bIconMax)
    {
    iSizeX = m_psi->m_rCLOSE.right - m_psi->m_rCLOSE.left;
    iSizeY = m_psi->m_rCLOSE.bottom - m_psi->m_rCLOSE.top;
    r1.left = r.right - 6 - 2 * iSizeX - 2 * 2;
    r1.top = r.top+5;
    r1.right = r1.left + iSizeX;
    r1.bottom = r1.top + iSizeY;
    bRet = PtInRect(&r1,point);
    if (bRet == TRUE)
    {
    m_bMaxPushed= TRUE;
    goto L_FOUND;
    }
    }
    if (m_bIconClose)
    {
    iSizeX = m_psi->m_rCLOSE.right - m_psi->m_rCLOSE.left;
    iSizeY = m_psi->m_rCLOSE.bottom - m_psi->m_rCLOSE.top;
    r1.left = r.right - 6 - iSizeX - 2;
    r1.top = r.top+5;
    r1.right = r1.left + iSizeX;
    r1.bottom = r1.top + iSizeY;
    bRet = PtInRect(&r1,point);
    if (bRet == TRUE)
    {
    m_bClosePushed= TRUE;
    goto L_FOUND;
    }
    }
    m_bMinPushed = FALSE;
    m_bMaxPushed = FALSE;
    m_bClosePushed = FALSE;
    m_bSysPushed = FALSE;
    L_FOUND:
    RepaintIcons();
    return;
    }
    恩。到这里,自绘边框的窗口就ok了。 

  • 相关阅读:
    关于烂代码的那些事(中)
    关于烂代码的那些事(上)
    关于烂代码的那些事(上)
    Maven学习总结(14)——Maven 多模块项目如何分工?
    Maven学习总结(14)——Maven 多模块项目如何分工?
    优秀Java程序员必备10招
    优秀Java程序员必备10招
    SSO单点登录学习总结(3)—— 基于CAS实现单点登录实例
    SSO单点登录学习总结(3)—— 基于CAS实现单点登录实例
    SSO单点登录学习总结(2)——基于Cookie+fliter单点登录实例
  • 原文地址:https://www.cnblogs.com/hcfalan/p/2269231.html
Copyright © 2020-2023  润新知