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

至此,无边框窗口阴影就大功告成了。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
如果想让MFC窗口去掉边框,可以使用以下代码: 1. 在窗口类的头文件中添加以下代码: ```cpp afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point); afx_msg void OnNcMouseMove(UINT nHitTest, CPoint point); afx_msg void OnNcLButtonUp(UINT nHitTest, CPoint point); ``` 2. 在窗口类的源文件中添加以下代码: ```cpp void CMyWnd::OnNcLButtonDown(UINT nHitTest, CPoint point) { // 获取窗口的位置和大小 CRect rect; GetWindowRect(rect); // 获取鼠标相对于窗口的坐标 ScreenToClient(&point); // 判断鼠标是否在窗口边框上 if (point.y < 5) { // 拖动窗口 SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, 0); } else if (point.y > rect.Height() - 5) { // 关闭窗口 SendMessage(WM_SYSCOMMAND, SC_CLOSE, 0); } else if (point.x < 5) { // 最小化窗口 SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0); } else if (point.x > rect.Width() - 5) { // 最大化窗口 SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, 0); } } void CMyWnd::OnNcMouseMove(UINT nHitTest, CPoint point) { // 改变鼠标样式 CRect rect; GetWindowRect(rect); ScreenToClient(&point); if (point.y < 5) { SetCursor(LoadCursor(NULL, IDC_SIZEALL)); } else if (point.y > rect.Height() - 5) { SetCursor(LoadCursor(NULL, IDC_HAND)); } else if (point.x < 5) { SetCursor(LoadCursor(NULL, IDC_APPSTARTING)); } else if (point.x > rect.Width() - 5) { SetCursor(LoadCursor(NULL, IDC_ARROW)); } else { SetCursor(LoadCursor(NULL, IDC_ARROW)); } } void CMyWnd::OnNcLButtonUp(UINT nHitTest, CPoint point) { // do nothing } ``` 这段代码会响应窗口的非客户区鼠标事件,实现窗口的拖动、最大化、最小化和关闭。同时,它还会改变鼠标的样式,使得鼠标在窗口边缘时可以更好地交互。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值