当前位置:C++技术网 > 资讯 > WM_PAINT消息及背景擦除知识详解

WM_PAINT消息及背景擦除知识详解

更新时间:2016-01-06 22:32:42浏览次数:1+次

win32中的窗口过程函数,一般来讲,所处理的第二条消息是WM_PAINT。这条消息在Windows编程中极其重要。当窗口的客户区的部分或全部"无效"且必须"更新"时,应用程序将得到此通知。这也就意味着窗口必须被"重绘"。
但是何种情况下客户区会变为无效?当窗口被首次创建时,整个客户区都是无效的,因为此时应用程序尚未在该窗口上绘制任何东西。你可以断点调试看看,我之前试过,WM_PAINT处的断点会在窗口创建之前就运行。
在调整窗口尺寸时,窗口的客户区也会变为无效。当我们利用Create或CreateEx函数创建窗口时,并利用AfxRegisterWndClass创建我们自定义的窗口时,我们选择的CS_HREDRAW和CS_VREDRAW。这就指示windows当窗口尺寸发生变化时,整个窗口都宣布无效,在此之后,窗口过程将收到一条WM_PAINT消息。
对于WM_PAINT消息的处理几乎总是从调用BeginPaint函数开始的,以EndPaint函数结束。在BeginPaint调用期间,如果客户区的背景尚未被擦除,则windows会对其进行擦除。擦除背景时使用的画刷是在用于注册窗口类的WNDCLASS结构中的hbrBackGround字段中指定的。通常是软件内部封装的WNDCLASS字段hbrBackGround字段值。尽管窗口过程必须能够在收到WM_PAINT消息时更新整个客户区,但通常他只需要更新其中的一部分,最常见的就是更新其中的一个矩形区域。需要重新绘制的部分被称为"无效区域"或"更新区域"。windows内部为每一个窗口都保存了一个“绘制信息结构"。这个结构保存着一个可以覆盖无效区域的最小矩形的坐标和其他的一些信息。这个最小的矩形称为"无效矩形"。如果在窗口过程处理一条等候处理的WM_PAINT消息之前,客户区中的另一部分也失效了,那么windows将计算出一个覆盖这两个失效部分的新的无效区域和无效矩形,并更新绘制信息结构中的数据。windows不会再消息队列中放置多条WM_PAINT消息。
窗口过程可以通过调用InvalidateRect()函数来强制使自己的客户区中的一个矩形失效。我们可以用GetUpdateRect来获取这些坐标。
窗口过程在处理WM_PAINT消息时,在调用BeginPaint函数后,整个客户区会变成有效的。程序也可以通过调用ValidateRect函数来使客户区中的任意矩形变得有效。如果该函数调用结果是让整个无效区域都有效,那么当前消息队列的WM_PAINT消息就会被删除。
在这里,补充说下,在MFC中的OnPaint函数中,有没有封装beginpaint与endpaint两个字段?显然是没有的,我们需要显示的调用BeginPaint函数来宣告我们要开始"重绘"区域。同时,该函数会填充"绘制信息结构"的各个字段。也就是我们给这个函数传的参数,PAINTSTRUCT结构体就是我们的绘制信息结构,对于该函数的定义,请读者自行百度。当我们的程序调用BeginPaint函数时,WIindows将自动填充这个结构的字段,之前也说过。但是程序只能够使用前三个字段,其他的供windows内部调用。在WINMAIN初始化时,Windows就使用用于注册窗口类的WNDCLASS结构中的hbrBackGround字段所指定的画刷来擦除背景。第三个字段是rcPaint,也就是需要擦除的区域。也就是说,windows将绘制,重绘仅仅限制在了这个区域里面。
我们知道有三个函数可以发出擦除命令:Invalidate(),InvalidateRect(),UpdateWindow(),或许还有,但不管其他,我们用函数强制发出"重绘"命令时,最好使用Invalidate函数,就因为它是对整个客户区进行重绘。而不仅仅局限于某一区域,这是很万全的做法。
下面我们实例试试:
对于单文档工程,OnDraw函数对我们的绘制消息进行响应,而不是OnPaint函数,那么我们建立两个子菜单,分别进行消息响应,当你执行两个消息时,也就是执行完一个,在执行一个时,这个时候,之前的消息响应结果还在,这就不行了,因此,我们完善这个不足:

void CGDIView::OnDraw(CDC* /*pDC*/)
{
	CGDIDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	PAINTSTRUCT ps;
	BeginPaint(&ps);
	if(i==1)
	{
		Graphics graphics(this->m_hWnd);
	Pen redPen(Color::Red,3);
	Rect ellipseRect(10,20,200,100);
	REAL startAngle=45.0f;
	REAL sweepAngle=90.0f;
	graphics.DrawArc(&redPen,ellipseRect,startAngle,sweepAngle);
	graphics.DrawRectangle(&Pen(Color::Black),ellipseRect);
	}
	if(i==2)
	{
		Graphics graphics(this->m_hWnd);
	Pen blackPen(Color(255,0,0,0),3);
	Rect ellipseRect(0,0,200,100);
	REAL startAngle=45.0f;
	REAL sweepAngle=90.0f;
	graphics.DrawPie(&blackPen,ellipseRect,startAngle,sweepAngle);
	}
	EndPaint(&ps);
	// TODO: 在此处为本机数据添加绘制代码
}
void CGDIView::OnGdi1()
{
	// TODO: 在此添加命令处理程序代码
	i=1;
	Invalidate();
}


void CGDIView::OnGdi2()
{
	// TODO: 在此添加命令处理程序代码
	i=2;
	Invalidate();
}

运行之后,你会发现,不仅满足了之前的情况,而且,当你随便拉伸客户区时,代码的结果依旧还在......