开发界面之窗口边框重绘

13 篇文章 0 订阅

基于上篇已经解决了富图软件窗口child的效果。这边主要仿照其重绘边框——即活动窗口边框高亮,本文不讲标题栏/border重绘(难度是比Client绘制要难,但是社区也有人做了相应的demo),基于富图考虑,它不要标题栏,我们也用无边框窗口绘制就好了。

1.边框及标题栏绘制

void CDlgFrm::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	// TODO:  在此处添加消息处理程序代码
	// 不为绘图消息调用 CDialogEx::OnPaint()
	
	//画边框
	HPEN newPen = CreatePen(PS_SOLID, 2, m_colFrm);
	HPEN oPen = (HPEN)dc.SelectObject(newPen);
	CRect rc, rc2;
	GetClientRect(rc);
	rc.left = rc.top = 2;
	dc.Rectangle(rc);
	dc.SelectObject(oPen);
	//画标题栏
	oPen = (HPEN)dc.SelectObject(GetStockObject(NULL_PEN));
	HBRUSH newBrsh = CreateSolidBrush(RGB(130, 130, 130));
	HBRUSH oldBrsh = (HBRUSH)dc.SelectObject(newBrsh);
	dc.Rectangle(3, 3, rc.Width()+1, 33);
	dc.SelectObject(oldBrsh);
	::DeleteObject(newBrsh);
	dc.SelectObject(oPen);
	//画标题文字
	if (m_strTitle.IsEmpty() == FALSE)
	{
		rc = CRect(0, 0, 100, 30);
		dc.SetTextColor(RGB(255, 250, 250));
		dc.SetBkMode(TRANSPARENT);
		dc.DrawText(m_strTitle, rc, DT_VCENTER | DT_CENTER | DT_SINGLELINE);
	}
	
}
2.此时还不能move/resize窗口,于是重写OnNcHitTest

LRESULT CDlgFrm::OnNcHitTest(CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值
	UINT nHitTest = CDialogEx::OnNcHitTest(point);

	CPoint pt(0, 0);
	ClientToScreen(&pt);
	if (nHitTest == HTCLIENT && point.y - pt.y < 32)
	{
		nHitTest = HTCAPTION;
	}
	RECT rcWindow;
	::GetWindowRect(m_hWnd, &rcWindow);
	// 最好将四个角的判断放在前面  
	if (point.x <= rcWindow.left + RESIZE_REGION_SIZE && point.y <= rcWindow.top + RESIZE_REGION_SIZE)
		return HTTOPLEFT;
	else if (point.x >= rcWindow.right - RESIZE_REGION_SIZE && point.y <= rcWindow.top + RESIZE_REGION_SIZE)
		return HTTOPRIGHT;
	else if (point.x <= rcWindow.left + RESIZE_REGION_SIZE && point.y >= rcWindow.bottom - RESIZE_REGION_SIZE)
		return HTBOTTOMLEFT;
	else if (point.x >= rcWindow.right - RESIZE_REGION_SIZE && point.y >= rcWindow.bottom - RESIZE_REGION_SIZE)
		return HTBOTTOMRIGHT;
	else if (point.x <= rcWindow.left + RESIZE_REGION_SIZE)
		return HTLEFT;
	else if (point.x >= rcWindow.right - RESIZE_REGION_SIZE)
		return HTRIGHT;
	else if (point.y <= rcWindow.top + RESIZE_REGION_SIZE)
		return HTTOP;
	else if (point.y >= rcWindow.bottom - RESIZE_REGION_SIZE)
		return HTBOTTOM;
	return nHitTest;
}
这里本人发现一个有趣的问题,当窗口为子窗口的时候,至此,是可以实现移动/resize了。但是如果你modify成popup了,只能移动,不能resize。实质是popup会丢失消息,于是手动加上

void CDlgFrm::OnNcLButtonDown(UINT nHitTest, CPoint point)
{
	// TODO:  在此添加消息处理程序代码和/或调用默认值

	CDialogEx::OnNcLButtonDown(nHitTest, point);
	if (nHitTest == HTTOP)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_TOP, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTBOTTOM)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOM, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTLEFT)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_LEFT, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTRIGHT)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_RIGHT, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTTOPLEFT)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPLEFT, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTTOPRIGHT)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPRIGHT, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTBOTTOMLEFT)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMLEFT, MAKELPARAM(point.x, point.y));
	else if (nHitTest == HTBOTTOMRIGHT)
		SendMessage(WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMRIGHT, MAKELPARAM(point.x, point.y));
}
这是popup窗口resize也ok了。

3.窗口resize阴影严重需要update

void CDlgFrm::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO:  在此处添加消息处理程序代码
	if (m_pDlg)
	{
		//::SendMessage(m_pDlg->m_hWnd, WM_SIZE, nType, MAKELPARAM(cx, cy));
		m_pDlg->SetWindowPos(NULL, 0, 0, cx - 4, cy - 32 - 2, SWP_NOMOVE);
		m_pDlg->Invalidate();
	}
	Invalidate();

}
其实至此,窗口绘制是完了,但是如果窗口中有子窗口或者控件,你会发现,当缩小到控件交叉时,会有边框被控件覆盖的效果如下图:

解决上图有两种方法:

1.使用自己管理的透明对话框(边框不透明)去覆盖在窗口上,然后让该对话框跟着Active窗口移动,resize以及窗口非Active时隐藏。

CRect rc;
	GetWindowRect(rc);
	if (m_dlgBorder.Create(CDlgBorder::IDD, this))
	{
		m_dlgBorder.m_hWndOwner = m_hWnd;
		m_dlgBorder.SetWindowPos(this, 0, 0, rc.Width(), rc.Height(), SWP_NOZORDER);
		m_dlgBorder.ModifyStyleEx(NULL, WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT);
		SetWindowLong(m_dlgBorder.m_hWnd, GWL_EXSTYLE, 
			GetWindowLongPtr(m_dlgBorder.m_hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
		m_dlgBorder.SetLayeredWindowAttributes(RGB(255, 255, 255), (255 * 0) / 100, LWA_COLORKEY/*LWA_ALPHA*/);
		m_dlgBorder.ShowWindow(SW_SHOW);
		int a = 0;
	}
注意该窗口要透明一定是popup属性,扩展属性必须有LAYERED|NOACTIVATE|TRANSPARENT,最后
SetLayeredWindowAttributes(...)
设置成透明。至于透明窗口中的代码就是绘制边框/重绘刷新了

void CDlgBorder::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	// TODO:  在此处添加消息处理程序代码
	// 不为绘图消息调用 CDialogEx::OnPaint()
	if (IsWindow(m_hWndOwner))
	{
		HWND m_hOwnerParent = ::GetParent(m_hWndOwner);
		HPEN newPen = CreatePen(PS_SOLID, 2, RGB(0, 255, 0));
		HPEN oPen = (HPEN)dc.SelectObject(newPen);
		CRect rc,rc2;
		GetClientRect(rc);
		rc.left = rc.top = 2;
		::GetClientRect(m_hOwnerParent, rc2);
		ClientToScreen(rc2);
		//if (rc.TopLeft)
		//dc.MoveTo()
		dc.Rectangle(rc);
		dc.SelectObject(oPen);
	}

}

void CDlgBorder::DoPaintBorder()
{
	CRect rc;
	if (IsWindow(m_hWndOwner))
	{
		::GetWindowRect(m_hWndOwner, rc);
		SetWindowPos(0, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER);
	}
}每次resize,move后调用DoPaintBorder更新窗口位置大小即可。

但是当窗口移动到边框的时候因为popup属性还会显示出来,所以,还要将该窗口修复一下,方式两种,1,自己计算边框控制绘图越界。2,将透明窗口设置成该窗口的父窗口的子窗口(也是该窗口的兄弟窗口)即可。

2.使用父窗口重绘边框,再在其客户区放置不可移动的子窗口,再在子窗口上放控件。相当于多嵌套一层子窗口。然后让子窗口跟着一起resize。

如文章开始提到的封装一个CDlgFrm当容器,但要注意的是OnSize中一定要将其子窗口一起resize。代码上面已经提供,否则还是会出现边框被子窗口遮挡的现象。最后上个效果图

标题栏再加上关闭按钮:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值