2016-01-15 10 views
8

Ho seguito la guida di seguito per creare un frame Aero personalizzato utilizzando l'API DWM.Come illuminare il minimo. tasto massimo e chiudi?

Custom Window Frame Using DWM

Il mio lavoro:

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

Risultato:

enter image description here

ho scoperto il minimo, massimo e pulsante di chiusura che non sarà brillava quando sposto il mouse su questi pulsanti.

enter image description here

Situazione generale:

enter image description here

Come risolvere questo problema?

migliori saluti,

+0

Bisogna seguire i movimenti del mouse e fai da te. –

risposta

6

DwmDefWindowProc è necessario per gestire i pulsanti didascalia. Da msdn:

Per il test di successo del pulsante Titolo, DWM fornisce la funzione DwmDefWindowProc . Per eseguire correttamente il test dei pulsanti didascalia negli scenari di frame personalizzati , i messaggi devono essere passati prima a DwmDefWindowProc per la gestione di . DwmDefWindowProc restituisce TRUE se un messaggio viene gestito e FALSE se non lo è. Se il messaggio non viene gestito da DwmDefWindowProc, , l'applicazione deve gestire il messaggio stesso o passare il messaggio su DefWindowProc.

In MFC si può lavorare come segue:

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

ho aggiunto una correzione con GetForegroundWindow(), perché la funzione HitTestNCA da MSDN esempio è sbagliato, non restituisce HTCLIENT quando dovrebbe. Quindi, quando un'altra finestra ha il focus, non cambierà le finestre con il clic del mouse nell'area client.

Inoltre, v'è una perdita nel OnNcPaint:

CDC* dc = GetWindowDC(); 

Ogni volta che si chiama GetWindowDC() si dovrebbe essere seguita da ReleaseDC. O semplicemente usa CWindowDC che ha la pulizia automatica. In realtà non è necessario eseguire l'override di OnNcPaint perché il frame è stato esteso a "area client".

Ecco un esempio completo:

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