mfc如何快速實現無邊框視窗陰影效果

NO IMAGE

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::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的處理。

至此,無邊框視窗陰影就大功告成了。