如何发光最小化。最大和关闭按钮?

时间:2016-01-15 01:32:23

标签: c++ winapi mfc

我按照以下指南使用DWM API创建自定义Aero Frame。

Custom Window Frame Using DWM

我的工作:

void CMainFrame::OnActivate(UINT nState,CWnd* pWndOther,BOOL bMinimized )
{
    CFrameWnd::OnActivate(nState,pWndOther,bMinimized);
    BOOL fDwmEnabled = FALSE;
    if (SUCCEEDED(DwmIsCompositionEnabled(&fDwmEnabled)))
    {
        if(nState == WA_ACTIVE )
        {
            MARGINS margins ={-1};
            HRESULT hr = DwmExtendFrameIntoClientArea(m_hWnd, &margins);
            if (!SUCCEEDED(hr));
        }
    }
}

void CMainFrame::OnNcPaint(){
    RECT rcClient;
    GetWindowRect(&rcClient);
    // Inform the application of the frame change.
    SetWindowPos( 
             NULL, 
             rcClient.left, rcClient.top,
             RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
             SWP_FRAMECHANGED);
    CFrameWnd::OnNcPaint();
    CDC* dc = GetWindowDC();
    dc->FillSolidRect(0,0,RECTWIDTH(rcClient),RECTHEIGHT(rcClient),RGB(0,0,0));
}

 LRESULT CMainFrame::OnNcHitTest(CPoint p)
 {
    LRESULT r ;
    r = CFrameWnd::OnNcHitTest( p);      
    if(r == HTMINBUTTON || r == HTMAXBUTTON || r == HTCLOSE)
        return r;
    else
        r = HitTestNCA(m_hWnd,p); // this function is direct copied from above link.
     return r;
 }

结果:

enter image description here

当我在这些按钮上移动鼠标时,我发现了不会发光的最小,最大和关闭按钮。

enter image description here

一般情况:

enter image description here

如何解决这个问题?

最诚挚的问候,

1 个答案:

答案 0 :(得分:6)

处理字幕按钮需要

DwmDefWindowProc。来自msdn:

  

对于字幕按钮命中测试,DWM提供DwmDefWindowProc   功能。要正确地测试自定义框架中的标题按钮   方案,首先应将消息传递给DwmDefWindowProc   处理。如果处理了消息,则DwmDefWindowProc会返回TRUE   FALSE如果不是。如果DwmDefWindowProc未处理该邮件,   您的应用程序应处理消息本身或传递消息   到DefWindowProc

在MFC中可以解决如下问题:

LRESULT cframeWnd::OnNcHitTest(CPoint p)
{
    BOOL dwm_enabled = FALSE;
    if (SUCCEEDED(DwmIsCompositionEnabled(&dwm_enabled)))
    {
        LRESULT result = 0;
        if (!DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y), &result))
            result = HitTestNCA(m_hWnd, p);

        if (result == HTNOWHERE && GetForegroundWindow() != this)
        {
            return HTCAPTION;
        }

        return result;
    }

    return CWnd::OnNcHitTest(p);
}

我添加了GetForegroundWindow()的修补程序,因为来自MSDN示例的HitTestNCA函数是错误的,它不应该返回HTCLIENT。因此,当另一个窗口具有焦点时,在客户区域中单击鼠标时不会切换窗口。

此外,OnNcPaint

出现泄漏
CDC* dc = GetWindowDC();

每当调用GetWindowDC()时,都应该跟ReleaseDC。或者只使用具有自动清理功能的CWindowDC。您实际上不需要覆盖OnNcPaint,因为框架已扩展到“客户区”。

以下是一个完整的例子:

class cglassWnd : public CWnd
{
    void    OnNcCalcSize(BOOL, NCCALCSIZE_PARAMS FAR*);
    LRESULT OnNcHitTest(CPoint p);
    void    OnNcMouseLeave();
    int     OnCreate(LPCREATESTRUCT lpCreateStruct);
    void    OnActivate(UINT state, CWnd* otherWnd, BOOL minimized);
    void    OnPaint();
    CRect   borders;
    int     titlebar_height;
    DECLARE_MESSAGE_MAP()
public:
    cglassWnd();
};

BEGIN_MESSAGE_MAP(cglassWnd, CWnd)
    ON_WM_NCHITTEST()
    ON_WM_NCCALCSIZE()
    ON_WM_NCMOUSELEAVE()
    ON_WM_ACTIVATE()
    ON_WM_CREATE()
    ON_WM_PAINT()
END_MESSAGE_MAP()

cglassWnd::cglassWnd()
{
    BOOL dwm_enabled = FALSE;
    DwmIsCompositionEnabled(&dwm_enabled);
    if (!dwm_enabled)
        TRACE("Error: don't use this class, add error handling...");

    //modified height for the new title bar
    titlebar_height = 60;
}

int cglassWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    int res = CWnd::OnCreate(lpCreateStruct);

    //find border thickness
    borders = { 0,0,0,0 };
    if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_THICKFRAME)
    {
        AdjustWindowRectEx(&borders,
            GetWindowLongPtr(m_hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
        borders.left = abs(borders.left);
        borders.top = abs(borders.top);
    }
    else if (GetWindowLongPtr(m_hWnd, GWL_STYLE) & WS_BORDER)
    {
        borders = { 1,1,1,1 };
    }

    //Extend caption in to client area
    MARGINS margins = { 0 };
    margins.cyTopHeight = titlebar_height;
    DwmExtendFrameIntoClientArea(m_hWnd, &margins);

    SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);

    return res;
}

void cglassWnd::OnPaint()
{
    CPaintDC dc(this);

    //paint titlebar area (this used to be the non-client area)
    CRect rc;
    GetClientRect(&rc);
    rc.bottom = titlebar_height;

    //see MSDN reference for explanation of this code
    //upside-down bitmap is for the sake of DrawThemeTextEx
    CDC memdc;
    memdc.CreateCompatibleDC(&dc);
    BITMAPINFOHEADER infhdr = { sizeof(infhdr), rc.right, -rc.bottom, 1, 32 };
    HBITMAP hbitmap = CreateDIBSection(dc,(BITMAPINFO*)(&infhdr),DIB_RGB_COLORS,0,0,0);
    auto oldbitmap = memdc.SelectObject(hbitmap);

    //do extra titlebar painting here
    //for example put DrawThemeTextEx for window's name

    dc.BitBlt(0, 0, rc.Width(), rc.Height(), &memdc, 0, 0, SRCCOPY);
    memdc.SelectObject(oldbitmap);
    DeleteObject(hbitmap);

    //begin normal paint
    //The new client area begins below titlebar_height which we define earlier
    GetClientRect(&rc);
    rc.top = titlebar_height;
    dc.FillSolidRect(&rc, RGB(128, 128, 255));
}

void cglassWnd::OnNcCalcSize(BOOL validate, NCCALCSIZE_PARAMS FAR* sz)
{
    if (validate)
    {
        sz->rgrc[0].left += borders.left;
        sz->rgrc[0].right -= borders.right;
        sz->rgrc[0].bottom -= borders.bottom;
    }
    else
    {
        CWnd::OnNcCalcSize(validate, sz);
    }
}

LRESULT cglassWnd::OnNcHitTest(CPoint pt)
{
    LRESULT result = 0;
    //handle close/minimize/maximize button
    if (DwmDefWindowProc(m_hWnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y), &result))
        return result;

    //cursor is over the frame or client area:
    result = CWnd::OnNcHitTest(pt);
    if (result == HTCLIENT)
    {
        ScreenToClient(&pt);
        if (pt.y < borders.top) return HTTOP;
        if (pt.y < titlebar_height) return HTCAPTION;
    }
    return result;
}

void cglassWnd::OnNcMouseLeave()
{
    //This is for close/minimize/maximize/help buttons
    LRESULT result;
    DwmDefWindowProc(m_hWnd, WM_NCMOUSELEAVE, 0, 0, &result);
    CWnd::OnNcMouseLeave();
}

void cglassWnd::OnActivate(UINT state, CWnd* otherWnd, BOOL minimized)
{
    CWnd::OnActivate(state, otherWnd, minimized);
    Invalidate(FALSE);
}