如何发光的最小。最大和关闭按钮? [英] How to glow the minimum. maximum and close button?
问题描述
我按照以下指南使用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.
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 toDwmDefWindowProc
for handling.DwmDefWindowProc
returnsTRUE
if a message is handled andFALSE
if it is not. If the message is not handled byDwmDefWindowProc
, your application should handle the message itself or pass the message ontoDefWindowProc
.
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屋!