📄 11.2.2 onpaint与 ondraw.txt
字号:
11.2.2 OnPaint与 OnDraw
前面已经提过 OnDraw函数是一个虚函数,另外,在窗口重绘时会发送一个 WM PAINT消息,如果想
让图形在窗口中始终都能显示出来,就可以将图形的给制操作放置在该消息的响应函数 COnPainO
中。而 OnDraw函数并不是 WM-PAINT消息的响应函数,为什么它会在窗口重绘过程中被调用呢?可以
看一下 Graphic程序的视类的基类: CView类对 WMYAEVT消息的响应函数: OnPaint的实现。该函数
的实现源代码位于安装 VC时微软提供的源文件: VIEWCORE.CPP中,其路径是: VC安装目录
\Microsoft Visua1 Studio\VC98\1σC\SRC,在此文件中 OnPaint函数的定义代码如例 11-5所示。
例 11-5
void CView ::OnPaint()
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc) ;
OnDraw (&dc) ;
读者可以在这个函数的定义处设置一个断点,然后调试运行Graphic程序,结果可以发现程序将停留
在此断点处。
在上述如例 11-5所示代码中, OnPaint函数首先利用 CPaintDC类构造一个设备上下文对象: dc。
但在第一章中曾提到,在响应 WM PAINT消息时,如果想得到 DC句柄,只能调用 BeginPaint函数,
而如果想释放这个 DC
句柄的话,应调用 EndPaint函数。然而在这里并没有看图 1 1. 8 CPaintDC类继承层次结构 到这
两个函数的调用,只是利用 CPaintDC类构造了一个DC对象,根据如图 1 1. 8所示的 CPaintDC类的
派生层次结构,可以知道 CPaintDC是从 CDC类派生的设备上下文类。
实际上, CPaintDC类将对 BeginPaint和 EndPaint这两个函数的调用封装到了其构造方法和析构方
法中,在构造时执行 BeginPaint函数,在析构时执行 EndPaint函数。也就是说, CPaintDC对象仍
是通过调用 BeginPaint函数得到 DC句柄,当不再需要这个 DC时,该对象仍是通过调用 EndPaint
函数释放 DC。井且 CPaintDC对象只能够在响应 WM PAINT 消息时使用,通常是在 OnPaint函数中
使用。如果在程序中其他地方想要使用 DC句柄时,只能利用 GetDC和 ReleaseDC函数来得到所需的
DC和释放 DC。
在上述如例 11-5所示的 OnPaint函数中,接着调用了 OnPrepareDC函数,这是一个虚函数,在为屏
幕显示而调用 OnDraw之前,以及在打印或打印预览过程中调用 On阳nt函数打印每一页文档之前,
框架都会调用该函数。如果是因屏幕显示而被调用, OnPrepareDC 函数的默认实现就是什么也不做,
但是它在派生类中被重写,例如,在 CScrollView类中被重写,用以调整设备上下文的属性。因此,
如果你重写了 OnPrepareDC函数,则在该函
数的开始处总是应该调用基类的 OnPrepareDC函数。
如例 11-5所示的 OnPaint函数接下来调用 OnDraw函数,这是一个虚函数,而这时是与
CGraphicView类相关的视类窗口需要更新,因此这时向框架传递的视类指针就是子类 :
CGraphicView类的指针。在程序运行时,根据多态性原理,这里调用的实际上是 CGraphicView类的
OnDraw函数。为了验证这一点,读者可以在CGraphicView类的OnDraw函数处设置一个断点,然后调
试运行 Graphic程序,将会发现程序首先进入 CView类的 OnPaint函数,然后进入到 CGraphicView
类的 OnDraw函数。
也就是说,因为基类 CView在响应WM PAINT消息的函数 : OnPaint中调用了 OnDraw函数,所以程序
视类的子类中的 OnDraw函数才会被调用。给程序员的感觉好像是 :在 MFC程序中, AppWizard自动
生成的视类的 OnDraw函数就是专门用来重绘窗口的。实际上, MFC程序的窗口重绘过程与第一章中
讲述的 Win32程序窗口重绘过程是一样的,只是前者将重绘的过程封装成一个函数 : OnPaint了。
在 OnPaint函数中调用 OnDraw函数,主要是为程序员提供方便,让我们可以在此函数中自行进行图
形的绘制。
当然,我们也可以在 CGraphicView类中增加WM PAINT消息的响应函数,默认的函数代码如例 11-6
所示。
例 11-6
void CGraph工cView : :OnPa工 nt ()
CPa工ntOC dc(this) ; // device context for paint工 ng
/ / TOOO : Add your message handler code here
/ / 0 0 not call CView : :Onpaint() for painting messages
可以发现此时 CGraphicView类的 OnPaint函数中并没有调用 OnDraw函数。读者可以调试运行这时
的 Graphic程序,看看这时程序还会不会调用 CGraphicView类的 OnDraw函数。读者可以在新添加
的这个 CGraphicView类的 OnPaint函数内设置一个断点,然后调试运行 Graphic程序,将会发现程
序进入到了这个 OnPaint函数,但始终没有进入到 OnDraw函数。可见此时 OnDraw函数不会被调用,
这是因为在我们自己捕获的 WM PAINT消息中没有调用 OnDraw函数。当然我们也可以在这个
OnPaint函数中调用 OnDraw函数,井在调用之前添加 OnPrepareDC函数调用 (之所以要调用这个函
数的原因,下面将讲述 ),即 CGraphicView类的 OnPaint函数的代码如例 11 -7所示。
例 11-7
void CGraphicView: :OnPaint()
CPai ntDC dc (th工 s) ; // device context for painting
/ / TOOO : Add your message handler code here OnPrepareDC(&dc) ; OnDraw (&dc ) ;
// 00 not call CScrollView : :OnPaint() for painting messages
然后再次调试运行 Graphic程序,就会发现这时它将调用 OnDraw函数。这一过程与在基类: CView
的 OnPaint函数中调用 OnDraw函数是一样的。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -