Как накалить минимум. максимальное и кнопка "Закрыть"?

я следовал ниже, чтобы создать пользовательский кадр Aero с помощью DWM API.

пользовательская оконная рама с помощью 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 ответов


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(), потому что HitTestNCA функция из примера MSDN неверна, она не возвращает 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);
}