OnPaint()函数与OnCreat()函数

在编写MFC程序的过程中,有两个常见的函数,OnCreat()与OnPaint(),有时候常常搞不清楚它们是怎么调用的,是在窗口创建之前还是窗口创建之后?它们都分别起到了什么作用?下面我们对其分别进行介绍。

1)OnPaint函数

主要功能: Windows发送WM_PAINT消息,程序响应并绘图;
发生原因: 诱使Windows发送WM_PAINT消息的原因可能有很多种,比如移动了一个窗口,由于窗口原先被遮盖的一部分显露出来了,或者是窗口的大小改变了。
工作机制: 当一个WM_PAINT抵达时,调用OnPaint处理函数,而OnPaint的绘制是从创建一个名为dc的CPaintDC的对象开始的;
打开方式:

开启OnPaint函数有下面三种选择:

1) 直接发送WM_PAINT消息,用PostMessage(),SendMessage()函数发送WM_PAINT消息。使用以上两函数发送WM_PAINT消息,能将WM_PAINT消息发送到WINDOWS程序消息队列中,当WINDOWS将WM_PAINT消息发送给具体的消息处理函数时,如果窗口的无效区域为空则WINDOWS将不理睬该消息。若存在无效区域,则调用窗口处理函数处理。要注意的这里需要存在无效区域,因此要调用2)中的函数使得窗体(或者部分)无效,其处理过程与2)相同,将WM_PAINT消息送入消息处理队列。与3)不同的是WM_PAINT并不立即处理;

2) 调用相应的API实现WM_PAINT消息的发送:Invalidate(),InvalidateRect(), InvalidateRgn():以上函数将窗口的特定区域标定为无效,当WINDOWS检测到窗口中存在无效区域时将向消息队列发送WM_PAINT 消息。我当时用的就是Invalidate()函数;

3) UpdateWindow():该函数调用后WINDOWS将向窗口发送一个非队列化的WM_PAINT消息,它不经过消息循环而直接发送给了窗口消息处理函数。如果窗口无效区域不存在,WINDOWS将不理睬该消息。注意这里因为要使得窗口无效区不存在,因此还是调用Invalidate(),InvalidateRect(), InvalidateRgn()函数,和2)中不同的是这里的WM_PAINT消息会被立即处理,而2)中是加入消息处理队列。

对于窗口程序,一般有个特点:窗口大部分的区域保持不变,只有不分区域需要重新绘制。如果将整个窗口全部刷新的画,就做了许多不必要的工作,因而,MFC采用了一套基于无效区的处理机制。在分析无效区处理之前,我们要明白一个现实,现在的机器还不够牛,如果够牛的话,我们干脆将整个窗口不断的重新绘制好了。事实上即使够牛也不行,对于一个单线程程序,通过一个while循环不断的刷新窗口,程序也无法相应其他消息(除非使用多线程),看来使用无效区的处理机制还是有其必然性的。

VC程序是基于消息机制的,你所做的任何操作,比如点击鼠标,拖动窗口,首先进入系统的消息队列。这里的系统消息队列包括多个程序的消息,系统再将消息发送给相应的程序。既然是队列,这就有一个先进先出的问题,屏幕上的无效区更新消息出现的频率就会特别高。比如当左上角更新的消息还没有处理,右下角更新的消息已经过来了。为了避免多次处理WM_PAINT消息,系统就将这些窗口更新消息合并到一条,只是将无效区范围变成包括这两次更新无效区范围在内的矩形区域。这样就减少了WM_PAINT消息的处理次数,提高了效率。

那么,在OnPaint消息处理函数中,又是怎样实现更新无效区的呢?首先,要明白MFC中所有绘图操作都是基于设备描述表(Device Context,简称DC)的,具体信息可参看任何一本VC教材。DC中包含了绘图设备的各种信息,对于屏幕绘图,其实就是有一块内存(显存),专门用来存放要显示到屏幕上的信息,显示器以85HZ的频率(我以前的显示器)将其内容刷新的屏幕上。这里就到了关键点,显示器的刷新是将显存中的内容完全更新到显示器上,不存在无效区处理的问题,那么,无效区的处理一定发生在DC的绘图处理上。事实确实如此,当程序调用OnPaint消息时,首先将无效区范围传递给DC,DC在进行绘图操作时,就只更新无效区范围内的信息,其他地方的不管,这就提高了效率。

简单起见,你可以使用2)中方案进行问题解决。

现在你明白OnPaint的处理是怎么一回事了吧?这里还想说一下Invalidate和UpdateWindow的区别。Invalidate在消息队列中加入一条WM_PAINT消息,其无效区为整个客户区。而UpdateWindow直接发送一个WM_PAINT消息,其无效区范围就是消息队列中WM_PAINT消息(最多只有一条)的无效区。效果很明显,调用Invalidate之后,屏幕不一定马上更新,因为WM_PAINT消息不一定在队列头部,而调用UpdateWindow会使WM_PAINT消息马上执行的,绕过了消息队列。如果你调用Invalidate之后想马上更新屏幕,那就加上UpdateWindow()这条语句。

2)OnCreat()函数

主要功能: Windows发送WM_CREAT消息,程序响应并表示一个
窗口正在生成;
发生原因:由Creat()函数调用;
工作机制: 当一个WM_CREAT抵达时,调用OnCreat()处理函数,OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等,Create()负责注册并产生窗口;

OnCreate是一个消息响应函数,是响应WM_CREATE消息的一个函数,而WM_CREATE消息是由Create函数调用的。
在view类中,Create 是虚函数由框架调用,是用来“生成一个窗口的子窗口”。OnCreate 函数是用来“表示一个窗口正在生成”。一个窗口创建(Create)之后,会向操作系统发送WM_CREATE消息,OnCreate()函数主要是用来响应此消息的。因为在MFC里面用一种消息映射的机制来响应消息,也就是可以用函数来响应相应的消息。就拿CMainFrame类来说,当窗口创建后会产生WM_CREATE消息,我们可以在OnCreate函数里实现我们要在窗口里面增加的东西,例如按扭,状态栏,工具栏等。这些子窗口一般是定义成类中的一个成员变量,因为要保证生命周期。一般以m_开头来表示成员(member)。
OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等,Create()负责注册并产生窗口。Create()不是对应于消息WM_CREATE的,OnCreate()才是。Create()只用于产生窗口,像动态创建控件中的Create()一样。

WM_CREATE是windows中一个窗口消息。

当一个应用程序创建窗口时,会通过消息处理函数。但是这个消息在CreateWindow函数返回之前被送到消息处理函数。例如,当要获取系统的字体时,我们可以先向这个窗口发送 WM_CREATE消息,以后字体不会改变了,程序会保存这个系统字体的消息!以后就可以用这个字体了!WM_CREATE返回值类型为LRESULT,如果一个程序处理这个消息,它应当返回0以使得窗口的创建过程得以继续。如果对于这个消息程序返回-1,窗口将会被销毁,并且CreateWindowEx或者CreateWindow函数将会返回一个值为NULL的句柄。

参考资料:
[1] http://blog.csdn.net/u013485792/article/details/21015135
[2] http://blog.csdn.net/foreverhuylee/article/details/21889025

你可能感兴趣的:(c++,MFC)