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

use*_*346 9 c++ winapi mfc

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

使用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;
 }
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

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

在此输入图像描述

一般情况:

在此输入图像描述

如何解决这个问题?

最好的祝福,

Bar*_*ani 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);
}
Run Code Online (Sandbox Code Playgroud)

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

此外,还有泄漏OnNcPaint:

CDC* dc = GetWindowDC();
Run Code Online (Sandbox Code Playgroud)

无论何时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);
}
Run Code Online (Sandbox Code Playgroud)