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::OnNcPaint()
{
// TODO: 在此處添加消息處理程序代碼
// 不爲繪圖消息調用
Invalidate();
UpdateWindow();
// CWnd::OnNcPaint();
}
不調用默認,並且刷新窗口。這樣有導致了另一個問題,就是窗口獲取和失去焦點的時候窗口陰影沒有變化。如果要達到有邊框窗口的效果,就繞不過上面提到的問題,所以再次換一種思路。
當窗口獲取焦點的時候加上陰影,失去焦點的時候去掉陰影。實際測試了一下,效果還比較理想。在"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的處理。至此,無邊框窗口陰影就大功告成了。