mfc如何快速实现无边框窗口阴影效果
mfc窗口当有边框的时候才会有阴影效果,怎么快速实现无边框窗口的阴影效果呢?
大部分的方法都是推荐使用分层联动的两个窗口来实现,但是这种处理方式稍显复杂,要处理两个窗口的代码。
现在介绍一种简单便捷的方法,其实这种方法也就是直接使用windows自带的阴影效果,所以简化了自己重绘阴影的繁琐问题。
首先介绍一下原理,这种无边框窗口并不是真正的无边框,只是将边框不显示,只显示出阴影效果。
所以步骤如下:
1 创建窗口(无论是动态创建还是使用资源)时,给窗口加上"WS_THICKFRAME"属性;
pWnd->CreateEx(WS_EX_LEFT,
"",
wndname,
WS_VISIBLE | WS_POPUP | WS_THICKFRAME,
CRect(0,0,100,100),
NULL,
0);
窗口创建之后可以看到窗口周围有一圈边框,边框外有阴影。
2 不显示边框。响应"WM_NCCALCSIZE"消息,并做处理。
void MyWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (lpncsp != NULL && GetWindowLong(GetSafeHwnd(),GWL_STYLE)&WS_THICKFRAME)
{
int xborder = ::GetSystemMetrics(SM_CXSIZEFRAME);
int yborder = ::GetSystemMetrics(SM_CYSIZEFRAME);
lpncsp->rgrc[0].left-= xborder-1;
lpncsp->rgrc[0].top-= yborder-1;
lpncsp->rgrc[0].right+= xborder-1;
lpncsp->rgrc[0].bottom+= yborder-2;
}
CWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
}
注意这一句:
lpncsp->rgrc[0].bottom+= yborder-2;
"如果这里也写成"-1"的话,阴影效果将消失。
好,现在可以看到,无边框的阴影效果已经出来了。
是不是就完成了呢?
其实还有几个问题要解决:
首先,这个窗口因为下边框多了一个像素,所以还是会有部分边框出现,视觉上的瑕疵可以忍受,但是这一个像素的宽度可以resize窗口是万万不能忍受的,特别是窗口显式不支持resize的情况下。
所以,我们要继续响应"WM_NCHITTEST"消息,一般无边框窗口我们都会自己重写这个消息响应,以模拟标题栏等等。
LRESULT MyWnd::OnNcHitTest(CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if(m_bAllowResize)
{
CRect rect;
GetWindowRect(&rect);
if(point.x <= rect.left+2)
return HTLEFT;
else if(point.x >= rect.right-2)
return HTRIGHT;
else if(point.y <= rect.top+2)
return HTTOP;
else if(point.y >= rect.bottom-2)
return HTBOTTOM;
else if(point.x <= rect.left+5 && point.y <= rect.top+5)
return HTTOPLEFT;
else if(point.x >= rect.right-5 && point.y <= rect.top+5)
return HTTOPRIGHT;
else if(point.x <= rect.left+5 && point.y >= rect.bottom-5)
return HTBOTTOMLEFT;
else if(point.x >= rect.right-5 && point.y >= rect.bottom-5)
return HTBOTTOMRIGHT;
}
return HTCLIENT;
}
细心的童鞋可能还发现因为我们在OnNcCalcSize中调整了窗口边框宽度,导致在窗口Resize和获取和失去焦点的时候,原本边框的位置会出现一圈灰色的边框,刷新不及时就会很明显。这个问题的真正解决方案在下还没有真正找到,所以希望有处理过的童鞋可以不吝赐教。
但是我们还是有方法来处理这个问题的,换一种思路绕过去就ok了。
Resize的问题,我们在"WM_NCPAINT"中处理;
void MyWnd::OnNcPaint()
{
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用
Invalidate();
UpdateWindow();
// CWnd::OnNcPaint();
}
不调用默认,并且刷新窗口。
获取和失去焦点问题,我们在"WM_NCACTIVATE"中去处理;
void MyWnd::OnNcActivate()
{
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用
Invalidate();
UpdateWindow();
// CWnd::OnNcActivate();
}
不调用默认,并且刷新窗口。
这样有导致了另一个问题,就是窗口获取和失去焦点的时候窗口阴影没有变化。如果要达到有边框窗口的效果,就绕不过上面提到的问题,所以再次换一种思路。
当窗口获取焦点的时候加上阴影,失去焦点的时候去掉阴影。实际测试了一下,效果还比较理想。在"WM_ACTIVATE"中处理;
void MyWnd::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
CWnd::OnActivate(nState, pWndOther, bMinimized);
// TODO: 在此处添加消息处理程序代码
if(nState == WA_INACTIVE)
{
m_nBorderHight -= 1;
ModifyStyle(WS_THICKFRAME,0);
}
else
{
m_nBorderHight += 1;
ModifyStyle(0,WS_THICKFRAME);
}
CRect rc;
GetClientRect(&rc);
Resize(rc.Width(),rc.Height());
}
关于代码中"m_nBorderHight"的处理,因为我们在OnNcCalcSize中对下边框减去了一个像素,所以这里要调整,m_nBorderHight表示自己模拟的窗口边框的下边框宽度。Resize其实就相当于OnSize的处理。
至此,无边框窗口阴影就大功告成了。