MFC SDI程序中OnDraw,OnPait,OnEraseBkGnd,Invalidate,UpdateWindow

来源:互联网 发布:极品统计软件下载 编辑:程序博客网 时间:2024/06/02 16:22
OnDraw:窗口初始输出,
OnCreate:窗口创建初始化期间,
OnPait:窗口重绘。

响应WM_PAINT,On_PRINT和On_PAINT都要调用On_Draw。故在OnCreate添加的对话框会在窗口显示前显示。因此一般的图形输出都写在OnDraw
OnDraw()OnPaint()好象兄弟俩,因为它们的工作类似。
当窗口改变后,会产生无效区域,这个无效的区域需要重画。一般Windows会发送两个消息WM_PAINT(通知客户区 有变化)和WM_NCPAINT(通知非客户区有变化)。非客户区的重画系统自己搞定了,而客户区的重画需要我们自己来完成。这就需要OnDraw() OnPaint()来重画窗口。
OnDraw()
OnPaint()有什么区别呢?
首先:我们先要明确CView类派生自CWnd类。而OnPaint()CWnd的类成员,同时负责响应WM_PAINT消息。这就是为什么你用VC成的程序代码时,在视图类只有OnDraw没有OnPaint的原因。而在基于对话框的程序中,只有OnPaintBOnDrawCVIEW虚函数,并且没有响应消息的功能。CView类响应重画的只有OnPaint函数,只是在函数中调用了虚函数OnDraw而以在子类中定义OnPaint消息处理函数后要么调用其类的OnPaint或者直接调用OnDraw不然OnDraw函数是不会执行的

其次,要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含 出设备(不单指你17寸的纯屏显示器,还包括打印机之类的输出设备)的绘图属性的描述MFC提供了CPaintDC类和CWindwoDC类来实时的响应,而CPaintDC支持重画。当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows  WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的DC对象响应该消息并调用视图的 OnDraw 成员函数通常我们不必编写重写的 OnPaint 处理成员函数
///CView默认的标准的重画函数void CView::OnPaint(){     CPaintDC dc(this);    OnPreparDC(&dc);    OnDraw(&dc); //调用了OnDraw}
既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序
///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。void CMyView::OnDraw( CDC* pDC ){     CMyDoc* pDoc = GetDocument();     CString s = pDoc->GetData(); // Returns a CString    CRect rect;    GetClientRect( &rect );     pDC->SetTextAlign( TA_BASELINE | TA_CENTER );     pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );}
最后:现在大家明白这哥俩之间的关系了吧。因此我们一般用OnPaint维护窗口的客户区(例如我们的窗口客户区加一个背景图片),用OnDraw维护视图的客户区(例如我们通过鼠标在视图中画图)。当然你也可以不按照上面规律来,只要达到目的并且没有问题,怎么干都成。

来源: <http://blog.csdn.net/tracing/article/details/2806156>

OnPaint()与OnDraw的区别:

1、Invalidate()和InvalidateRect()其实是触发对onPaint()函数的调用,   
OnPaint()函数调用OnDraw()函数,   
OnDraw函数还需要同时支持打印机输出。OnPaint()函数和OnPrint()函数都要调用OnDraw(),于是同样的绘图代码既可以用于屏幕输出也可以用于打印机输出   
在编程中,一般重载OnDraw()就可以了。如果定义了OnPaint()函数,并且在OnDraw()里面有要显示的内容,那么需要显示的调用OnDraw(),即OnDraw(&dc)。

2、OnDraw()是被OnPaint()调用的虚函数,在CView中定义为纯虚函数,因此必须被重载,其设备上下文由OnPaint()提供。   
OnPaint()是响应消息WM_PAINT的响应函数,默认实现中在,先调用BeginPaint(),最后调用EndPaint,OnDraw在BeginPaint与EndPaint间被调用。   

建议编程时使用OnDraw。 

3、CView封装了两个函数,OnPaint()与OnPrint(),分别对应WM_PAINT与WM_PRINT。MFC为了提供更标准简易的编程接口,所以又提供了OnDraw()这个函数。OnDraw()将被OnPaint()或OnPrint()调用,根据二者分别传进来的不同DC(Paint DC或Print DC),从而完成屏幕绘制或打印工作,而不需再为两种情况分别写代码。当然,如果你只关心屏幕绘制工作,而不关心打印问题,那你完全可以直接重载OnPaint()完成绘制,而不使用OnDraw()。


来源: <http://blog.csdn.net/yangsen2016/article/details/1620154>

OnEraseBkGnd&OnPaint

问题是这样产生的,在OnEraseBkGnd中,如果你不调用原来缺省的OnEraseBkGnd只是重画背景则不会有闪烁。而在OnPaint里面, 由于它隐含的调用了OnEraseBkGnd,而你又没有处理OnEraseBkGnd 函数,这时就和窗口缺省的背景刷相关了.缺省的OnEraseBkGnd操作使用窗口的缺省背景刷刷新背景(一般情况下是白刷),而随后你又自己重画背景造成屏幕闪动
另外一个问题是OnEraseBkGnd不是每次都会被调用的.如果你 调用Invalidate的时候参数为TRUE,那么在OnPaint里面隐含调用BeginPaint的时候产生WM_ERASEBKGND消息,如果参数 是FALSE,则不会重刷背景. 
 
所以解决方法有三个半
1.用OnEraseBkGnd实现,不要调用原来的OnEraseBkGnd函数. 
2.用OnPaint实现,同时重载OnEraseBkGnd,其中直接返回
3.用OnPaint实现,创建窗口时设置背景刷为空 
4.用OnPaint实现,但是要求刷新时用Invalidate(FALSE)这样 的函数.(不过这种情况下,窗口覆盖等造成的刷新还是要闪一,所以不是彻底的解决方法) 
都挺简单的.
------------------------------------------------------
在MFC中 任何一个window组件的绘图 都是放在这两个member function中,在设定上,OnEraseBkgnd()是用来画底图的,而OnPaint()是用来画主要对象的。
举例说明,一个按钮是灰色的,上面还有文字,则OnEraseBkgnd()所做的事就是把按钮画成灰色,而OnPaint()所做的事,就是画上文字。
 
既然这两个member function都是用来画出组件的,那为何还要分OnPaint()与 OnEraseBkgnd() 呢?
其实OnPaint() 与 OnEraseBkgnd() 特性是有差的
1. OnEraseBkgnd()的要求是快速 在里面的绘图程序最好是不要太耗时间
因为每当window组件有任何小变动,都会马上呼叫OnEraseBkgnd()
2. OnPaint() 是只有在程序有空闲的时候才会被呼叫
3. OnEraseBkgnd() 是在 OnPaint() 之前呼叫的
所以 OnPaint()被呼叫一次之前 可能会呼叫OnEraseBkgnd()好几次
如果我们是一个在做图形化使用者接口的人
常会需要把一张美美的图片设为我们dialog的底图
把绘图的程序代码放在OnPaint() 之中 可能会常碰到一些问题

比方说拖曳一个窗口在我们做的dialog上面一直移动,则dialog会变成灰色,直到动作停止才恢复。
这是因为每次需要重绘的时候 程序都会马上呼叫OnEraseBkgnd(),OnEraseBkgnd()就把dialog画成灰色,而只有动作停止之后,程序才会呼OnPaint(),这时才会把我们要画的底图贴上去。
 
这个问题的解法 比较差点的方法是把OnEraseBkgnd() 改写成不做事的function
如下所示
BOOL CMyDlg::OnEraseBkgnd(CDC* pDC){    return TRUE;}
以上本来是会呼叫CDialog::OnEraseBkgnd() 但是如果我们不呼叫的话,程序便不会画上灰色的底色了。
 
比较好的做法是直接将绘图的程序从OnPaint()移到OnEraseBkgnd()来做(注意:背景图是推荐这么做的,但是如果频繁计算耗费时间,则不合适)
如下所示
// m_bmpBKGND 为一CBitmap对象 且事先早已加载我们的底图// 底图的大小与我们的窗口client大小一致 BOOL CMyDlg::OnEraseBkgnd(CDC* pDC){CRect rc;GetUpdateRect(&rc);CDC srcDC;srcDC.CreateCompatibleDC(pDC);srcDC.SelectObject(m_bmpBKGND); pDC->BitBlt(rc.left,rc.top,rc.GetWidth(),rc.GetHeight(),&srcDC,rc.left,rc.top,SRCCOPY);return TRUE;}
特别要注意的是 取得重画大小是使用GetUpdateRect() 而不是GetClientRect()
如果使用GetClientRect() 会把不该重画的地方重画


Invalidate介绍  void Invalidate( BOOL bErase = TRUE );   该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。与UpdateWindow( )的区别UpdateWindow( )的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘
 
0 0