MFC非客户区自绘标题栏的实现(不断更新)

29 篇文章 0 订阅

某些情况下需要在对话框标题栏上添加按钮,在此mark下:

环境:win7 + vs2008 + sp1

项目:MFC对话框

步骤:

    1.  重写方法:DefWindowProc

            1.1 .h文件添加代码如下:

               a.  virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

                      // 添加:
                      virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);

                b.   重绘方法声明:

                        void DrawTitleBar(CDC *pDC);

                c.   按钮标识

                       CRect m_rtButtExit;    // 关闭按钮位置
                       CRect m_rtButtMax;    // 最大化按钮位置
                       CRect m_rtButtMin;    // 最小化按钮位置
                       CRect m_rtButtHel;    // 帮助按钮
                       CRect m_rtIcon;        // 图标位置

               

           1.2  .cpp文件中实现:

LRESULT CtestMyWndDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT lrst = CDialog::DefWindowProc(message, wParam, lParam);
    if (!::IsWindow(m_hWnd))
        return lrst;
    
    if (message==WM_MOVE
        ||message==WM_PAINT
        ||message==WM_NCPAINT
        ||message==WM_NCACTIVATE
        ||message == WM_NOTIFY)
    {
        CDC* pWinDC = GetWindowDC();
        if (pWinDC)
            DrawTitleBar(pWinDC);
        ReleaseDC(pWinDC);
    }
    return lrst;
}

           其中 DrawTitleBar方法如下: 

                 // 重绘标题栏
void CtestMyWndDlg::DrawTitleBar(CDC *pDC)
{
    if (m_hWnd)
    {
        CBrush Brush(RGB(0,0,0));
        CBrush* pOldBrush = pDC->SelectObject(&Brush);

        CRect rtWnd, rtTitle, rtButtons;
        GetWindowRect(&rtWnd);
        //取得标题栏的位置
        rtTitle.left = GetSystemMetrics(SM_CXFRAME);
        rtTitle.top = GetSystemMetrics(SM_CYFRAME);
        rtTitle.right = rtWnd.right - rtWnd.left - GetSystemMetrics(SM_CXFRAME);
        rtTitle.bottom = rtTitle.top + GetSystemMetrics(SM_CYSIZE);
        
        // 避免与原有标题栏重叠
        int nTitleTopDown = 0;
        CPoint point;
        //填充顶部框架
        point.x = rtWnd.Width();
        point.y = GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYFRAME)+20;
        pDC->PatBlt(0, nTitleTopDown, point.x, point.y, PATCOPY);
        //填充左侧框架
        point.x = GetSystemMetrics(SM_CXFRAME) + 1;
        point.y = rtWnd.Height();
        pDC->PatBlt(0, nTitleTopDown, point.x, point.y, PATCOPY);
        //填充底部框架
        point.x = rtWnd.Width();
        point.y = GetSystemMetrics(SM_CYFRAME) + 1;
        pDC->PatBlt(0, rtWnd.Height()-point.y, point.x, point.y, PATCOPY);
        //填充右侧框架
        point.x = GetSystemMetrics(SM_CXFRAME) + 1;
        point.y = rtWnd.Height();
        pDC->PatBlt(rtWnd.Width()-point.x, 0, point.x, point.y, PATCOPY);

        //绘制美化线条
        CBrush BrushLine(RGB(255, 255, 255));
        pDC->SelectObject(&BrushLine);
        point.x = rtWnd.Width() - 150;
        point.y = GetSystemMetrics(SM_CYFRAME) + nTitleTopDown + 2;
        pDC->PatBlt(GetSystemMetrics(SM_CXFRAME), point.y, point.x, 1, PATCOPY);
        point.y += 5;
        pDC->PatBlt(GetSystemMetrics(SM_CXFRAME), point.y, point.x, 1, PATCOPY);
        point.y += 5;
        pDC->PatBlt(GetSystemMetrics(SM_CXFRAME), point.y, point.x, 1, PATCOPY);
        point.x += 60;
        point.y += 12;
        pDC->PatBlt(point.x, point.y, 85, 1, PATCOPY);
        point.y += 5;
        pDC->PatBlt(point.x, point.y, 85, 1, PATCOPY);
        point.x -= 60 - GetSystemMetrics(SM_CXFRAME);
        point.y += 5;
        pDC->PatBlt(point.x, point.y, 145-GetSystemMetrics(SM_CXFRAME) , 1, PATCOPY);
        point.y -= 32;
        pDC->PatBlt(point.x, point.y, 1 , 32, PATCOPY);
        
        pDC->SelectObject(pOldBrush);

        //重画图标
        m_rtIcon.left = rtWnd.Width() - 130;
        m_rtIcon.top = GetSystemMetrics(SM_CYFRAME) + nTitleTopDown;
        m_rtIcon.right = m_rtIcon.left + 32;
        m_rtIcon.bottom = m_rtIcon.top + 32;
        ::DrawIconEx(pDC->m_hDC, m_rtIcon.left, m_rtIcon.top, m_hIcon,
            m_rtIcon.Width(), m_rtIcon.Height(), 0, NULL, DI_NORMAL);
        m_rtIcon.OffsetRect(rtWnd.TopLeft());


        //准备画XP风格按钮
        CBitmap* pBitmap = new CBitmap;
        CBitmap* pOldBitmap;
        CDC* pDisplayMemDC=new CDC;
        pDisplayMemDC->CreateCompatibleDC(pDC);

        //重画关闭button
        rtButtons.left = rtTitle.right - 19;
        rtButtons.top = rtTitle.top + nTitleTopDown;
        rtButtons.right = rtButtons.left + 19;
        rtButtons.bottom = rtButtons.top + 19;
        pBitmap->LoadBitmap(IDB_EXIT_NORMAL);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButtons.left, rtButtons.top, rtButtons.Width(), rtButtons.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        m_rtButtExit = rtButtons;
        m_rtButtExit.OffsetRect(rtWnd.TopLeft());
        pBitmap->DeleteObject();

        //重画最大化/恢复button
        rtButtons.right = rtButtons.left - 3;
        rtButtons.left = rtButtons.right - 19;
        if (IsZoomed())
            pBitmap->LoadBitmap(IDB_RESTORE_NORMAL);
        else
            pBitmap->LoadBitmap(IDB_MAX_NORMAL);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButtons.left, rtButtons.top, rtButtons.Width(), rtButtons.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        m_rtButtMax = rtButtons;
        m_rtButtMax.OffsetRect(rtWnd.TopLeft());
        pBitmap->DeleteObject();

        //重画最小化button
        rtButtons.right = rtButtons.left - 3;
        rtButtons.left = rtButtons.right - 19;
        pBitmap->LoadBitmap(IDB_MIN_NORMAL);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButtons.left, rtButtons.top, rtButtons.Width(), rtButtons.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        m_rtButtMin = rtButtons;
        m_rtButtMin.OffsetRect(rtWnd.TopLeft());
        pBitmap->DeleteObject();

        //重画帮助button
        rtButtons.right = rtButtons.left - 3;
        rtButtons.left = rtButtons.right - 19;
        pBitmap->LoadBitmap(IDB_HELP_NORMAL);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButtons.left, rtButtons.top, rtButtons.Width(), rtButtons.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        m_rtButtHel = rtButtons;
        m_rtButtHel.OffsetRect(rtWnd.TopLeft());
        pBitmap->DeleteObject();
        
        //重画caption
        int nOldMode = pDC->SetBkMode(TRANSPARENT);
        COLORREF clOldText=pDC->SetTextColor(RGB(255, 255, 255));
        pDC->SelectStockObject(SYSTEM_FIXED_FONT);
        rtTitle.left += 2;
        rtTitle.top += GetSystemMetrics(SM_CYSIZE) + nTitleTopDown;
        rtTitle.bottom = rtTitle.top + 30;
        CString m_strTitle;
        GetWindowText(m_strTitle);
        pDC->DrawText(m_strTitle, &rtTitle, DT_LEFT);
        pDC->SetBkMode(nOldMode);
        pDC->SetTextColor(clOldText);

        ReleaseDC(pDisplayMemDC);
        delete pDisplayMemDC;
        delete pBitmap;
    }
}

    2. 添加窗口消息OnNcLButtonDown - 非客户端鼠标左键按下事件

                  2.1 .h头文件中:

                                afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point);

                   2.2 .cpp 文件实现:

void CtestMyWndDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    // 4. 非客户区鼠标左键按下事件
    //检测各按钮是否按到
    if (m_rtIcon.PtInRect(point))
    {
        ::MessageBox(this->m_hWnd, L"ICON", L"", MB_OK);
    }
    else if (m_rtButtExit.PtInRect(point))
    {
        SendMessage(WM_CLOSE);
    }
    else if (m_rtButtMin.PtInRect(point))
    {
        SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM(point.x, point.y));
    }
    else if (m_rtButtHel.PtInRect(point))
    {
        ::MessageBox(this->m_hWnd, L"HELP", L"", MB_OK);
    }
    else if (m_rtButtMax.PtInRect(point))
    {
        if (IsZoomed())
            SendMessage(WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM(point.x, point.y));
        else
        {
            SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM(point.x, point.y));
            Invalidate();
        }
    }
    else if (!IsZoomed())
    {
        CDialog::OnNcLButtonDown(nHitTest, point);
    }
}

   3.  添加窗口消息 OnNcMouseMove - 非客户端鼠标移动事件

                  3.1 .h头文件中:

                                afx_msg void OnNcMouseMove(UINT nHitTest, CPoint point);

                  3.2 .cpp文件实现:

void CtestMyWndDlg::OnNcMouseMove(UINT nHitTest, CPoint point)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    // 3. 非客户区鼠标移动事件
    CDC* pDC = GetWindowDC();
    CDC* pDisplayMemDC=new CDC;
    pDisplayMemDC->CreateCompatibleDC(pDC);
    CBitmap* pBitmap = new CBitmap;
    CBitmap* pOldBitmap;
    CRect rtWnd, rtButton;

    if (pDC)
    {
        GetWindowRect(&rtWnd);

        //关闭button
        if (m_rtButtExit.PtInRect(point))
            pBitmap->LoadBitmap(IDB_EXIT_FOCUS);
        else
            pBitmap->LoadBitmap(IDB_EXIT_NORMAL);
        rtButton = m_rtButtExit;
        rtButton.OffsetRect(-rtWnd.left, -rtWnd.top);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButton.left, rtButton.top, rtButton.Width(), rtButton.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        pBitmap->DeleteObject();

        //最大化/恢复button
        if (m_rtButtMax.PtInRect(point))
        {
            if (IsZoomed())
                pBitmap->LoadBitmap(IDB_RESTORE_FOCUS);
            else
                pBitmap->LoadBitmap(IDB_MAX_FOCUS);
        }
        else
        {
            if (IsZoomed())
                pBitmap->LoadBitmap(IDB_RESTORE_NORMAL);
            else
                pBitmap->LoadBitmap(IDB_MAX_NORMAL);
        }
        rtButton = m_rtButtMax;
        rtButton.OffsetRect(-rtWnd.left, -rtWnd.top);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButton.left, rtButton.top, rtButton.Width(), rtButton.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        pBitmap->DeleteObject();

        //最小化button
        if (m_rtButtMin.PtInRect(point))
            pBitmap->LoadBitmap(IDB_MIN_FOCUS);
        else
            pBitmap->LoadBitmap(IDB_MIN_NORMAL);
        rtButton = m_rtButtMin;
        rtButton.OffsetRect(-rtWnd.left, -rtWnd.top);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButton.left, rtButton.top, rtButton.Width(), rtButton.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        pBitmap->DeleteObject();

        //帮助button
        if (m_rtButtHel.PtInRect(point))
            pBitmap->LoadBitmap(IDB_HELP_FOCUS);
        else
            pBitmap->LoadBitmap(IDB_HELP_NORMAL);
        rtButton = m_rtButtHel;
        rtButton.OffsetRect(-rtWnd.left, -rtWnd.top);
        pOldBitmap=(CBitmap*)pDisplayMemDC->SelectObject(pBitmap);
        pDC->BitBlt(rtButton.left, rtButton.top, rtButton.Width(), rtButton.Height(), pDisplayMemDC, 0, 0, SRCCOPY);
        pDisplayMemDC->SelectObject(pOldBitmap);
        pBitmap->DeleteObject();
    }
    
    ReleaseDC(pDisplayMemDC);
    ReleaseDC(pDC);
    delete pDisplayMemDC;
    delete pBitmap;
    CDialog::OnNcMouseMove(nHitTest, point);
}

4. 添加窗口消息OnNcCalcSize - 调整非客户端区域大小(根据需要可加可不加)

              4.1       .h 头文件

                      afx_msg void OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp);

              4.2        .cpp 实现文件

void CtestMyWndDlg::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    // 2. 调整标题栏高度
    lpncsp->rgrc[0].top += 26;
    CDialog::OnNcCalcSize(bCalcValidRects, lpncsp);
}

5. 添加窗口消息 OnSize - 取消原来对话框标题边框

             5.1    .h 头文件

                        afx_msg void OnSize(UINT nType, int cx, int cy);

            5.2    .cpp 实现文件


void CtestMyWndDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);

    // TODO: 在此处添加消息处理程序代码// Create a rgn without 3 pixels border
     CRect wrc;
     GetWindowRect(&wrc);
     wrc.OffsetRect(-wrc.left,-wrc.top);
     wrc.DeflateRect(3,3);
    //
     CRgn rgn;
     BOOL bl=rgn.CreateRectRgnIndirect(&wrc);
     if(bl) SetWindowRgn(rgn,TRUE);
     rgn.Detach();
}


http://bbs.csdn.net/topics/390624967

http://blog.csdn.net/chenlycly/article/details/5864367

http://www.xuebuyuan.com/213580.html

MFC(Microsoft Foundation Class)是一种用于开发Windows平台应用程序的类库,它提供了丰富的功能和接口来简化开发者的工作。MFC客户完美自绘是指在MFC应用程序中,开发者可以对客户(如标题栏边框等)进行完全自定义的绘制。这意味着开发者可以根据自己的需求和设计来美化和定制应用程序的外观。 MFC客户完美自绘实现需要开发者对MFC框架和Windows消息处理有一定的了解和掌握。通过重载窗口类的相应函数(如OnNcPaint、OnNcCalcSize等),开发者可以获取客户的绘制消息,并在这些消息的处理过程中进行自定义的绘制操作。此外,还可以通过修改窗口样式(style)和扩展风格(extended style)等方式来实现客户的自定义外观。 MFC客户完美自绘的优点在于可以实现高度的个性化定制,使应用程序更加独特和美观。开发者可以根据自己的设计风格和用户体验要求,完全自定义客户的外观和交互效果,从而提升应用程序的品质和吸引力。同时,这也为开发者提供了更大的发挥空间,可以实现更多创新和特色的功能。 总之,MFC客户完美自绘为开发者提供了丰富的自定义机会,使他们能够打造出更加独特和优秀的Windows应用程序。通过深入理解MFC框架和消息处理机制,开发者可以充分利用这一特性,为用户带来更好的体验和享受。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值