2016-01-15 7 views
8

Ich folgte unten Anleitung, um einen benutzerdefinierten Aero Frame mit DWM-API zu erstellen.Wie das Minimum zu glühen. Maximal- und Schließknopf?

Custom Window Frame Using DWM

Meine Arbeit:

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

Ergebnis:

enter image description here

ich das Minimum herausgefunden, Maximal- und Schließen-Schaltfläche, die glühte wird nicht, wenn ich die Maus bewegen auf diesen Tasten.

enter image description here

Allgemeine Situation:

enter image description here

Wie dieses Problem zu beheben?

Mit besten Grüßen,

+0

Sie haben Mausbewegungen zu verfolgen und do it yourself. –

Antwort

6

DwmDefWindowProc erforderlich Beschriftung Tasten zu behandeln. Von msdn:

Für Treffertests caption Taste stellt DWM die DwmDefWindowProc Funktion. Um die Beschriftungsschaltflächen in Szenarios mit benutzerdefinierten Rahmen richtig zu testen, sollten Nachrichten zuerst an DwmDefWindowProc für Behandlung übergeben werden. DwmDefWindowProc gibt TRUE zurück, wenn eine Nachricht verarbeitet wird, und FALSE, wenn dies nicht der Fall ist. Wenn die Nachricht nicht von DwmDefWindowProc, verarbeitet wird, sollte Ihre Anwendung die Nachricht selbst behandeln oder die Nachricht an DefWindowProc übergeben.

In MFC kann es funktionieren wie folgt:

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

Ich habe mit GetForegroundWindow() einen Fix, weil die HitTestNCA Funktion von MSDN Beispiel falsch ist, ist es nicht HTCLIENT zurück, wenn es sein sollte. Wenn also ein anderes Fenster den Fokus hat, wird es bei einem Mausklick im Client-Bereich nicht umgeschaltet.

Außerdem gibt es ein Leck in OnNcPaint:

CDC* dc = GetWindowDC(); 

Wenn GetWindowDC() sollte es von ReleaseDC gefolgt aufgerufen werden. Oder verwenden Sie einfach CWindowDC, die automatische Bereinigung hat. Sie müssen OnNcPaint nicht wirklich überschreiben, da der Rahmen auf "Client-Bereich" erweitert wurde.

Hier ist ein vollständiges Beispiel:

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