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

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

至此,無邊框窗口陰影就大功告成了。
發佈了33 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章