如何发光的最小。最大和关闭按钮? [英] How to glow the minimum. maximum and close button?

查看:309
本文介绍了如何发光的最小。最大和关闭按钮?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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





我发现最小值,最大值和关闭按钮,当我将鼠标移动到这些按钮时不会闪烁。





一般情况:





如何解决这个问题?



最好的问候,

$处理字幕按钮需要b $ b

解决方案

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 CWnd :: OnNcHitTest(p);
}



我添加了一个修复 GetForegroundWindow code>,因为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状态,CWnd * otherWnd,BOOL最小化);
void OnPaint();
边界;
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(错误:不要使用此类,添加错误处理...);

//为新标题栏修改高度
titlebar_height = 60;
}

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

//找到边框粗细
borders = {0,0,0,0};
if(GetWindowLongPtr(m_hWnd,GWL_STYLE)& WS_THICKFRAME)
{
AdjustWindowRectEx(& border,
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};
}

//将标题扩展到客户区
MARGINS marginins = {0};
marginins.cyTopHeight = titlebar_height;
DwmExtendFrameIntoClientArea(m_hWnd,& margin);

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

return res;
}

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

//绘制标题栏区域(以前是非客户区)
CRect rc;
GetClientRect(& rc);
rc.bottom = titlebar_height;

//查看MSDN参考来解释这个代码
//倒置位图是为了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);

//在这里做额外的标题栏
//例如为窗口的名称设置DrawThemeTextEx

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

//开始正常的绘画
//新的客户区开始于我们之前定义的titlebar_height之下
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 /最小化/最大化按钮
if(DwmDefWindowProc(m_hWnd,WM_NCHITTEST,0,MAKELPARAM(pt.x,pt.y),& result))
return result;

//游标超出框架或客户区:
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()
{
//这是用于close /最小化/最大化/帮助按钮
LRESULT result;
DwmDefWindowProc(m_hWnd,WM_NCMOUSELEAVE,0,0,& result);
CWnd :: OnNcMouseLeave();
}

void cglassWnd :: OnActivate(UINT state,CWnd * otherWnd,BOOL最小化)
{
CWnd :: OnActivate(state,otherWnd,minimize)
Invalidate(FALSE);
}


I followed below guide to create a custom Aero Frame using DWM API.

Custom Window Frame Using DWM

My work:

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

Result:

I found out the minimum, maximum and close button that will not be glowed when I move the mouse on these buttons.

General situation:

How to fix this problem?

Best Regards,

解决方案

DwmDefWindowProc is required to handle caption buttons. From msdn:

For caption button hit testing, DWM provides the DwmDefWindowProc function. To properly hit test the caption buttons in custom frame scenarios, messages should first be passed to DwmDefWindowProc for handling. DwmDefWindowProc returns TRUE if a message is handled and FALSE if it is not. If the message is not handled by DwmDefWindowProc, your application should handle the message itself or pass the message onto DefWindowProc.

In MFC it can work out as follows:

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

I added a fix with GetForegroundWindow(), because the HitTestNCA function from MSDN example is wrong, it doesn't return HTCLIENT when it should. So when another window has focus, it won't switch windows upon mouse click in client area.

Also, there is a leak in OnNcPaint:

CDC* dc = GetWindowDC();

Whenever GetWindowDC() is called it should be followed by ReleaseDC. Or just use CWindowDC which has automatic cleanup. You don't actually need to override OnNcPaint because frame has been extended to "client area".

Here is a full example:

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

这篇关于如何发光的最小。最大和关闭按钮?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆