一、CPaintDC与CClientDC
CPaintDC是从CDC派生出来的。在构造时自动调用CWnd::BeginPaint,析构时调用CWnd::EndPaint。通常CPaintDC用来响应WM_PAINT消息。一般应用在OnPaint函数.
CClientDC也是从CDC派生出来的.构造时自动调用GetDC函数,析构时自动调用ReleaseDC函数.一般应用于客户区窗口的绘制.
二、OnEraseBkgnd与OnPaint
在MFC中 任何一个window元件的绘图 都是放在这两个member function中,在设定上 OnEraseBkgnd()是用来画底图的 而OnPaint()是用来画主要物件的举例说明:
既然这两个member function都是用来画出元件的那为何还要分OnPaint() 与OnEraseBkgnd() 呢
其实OnPaint() 与OnEraseBkgnd() 特性是有差的
1.
2.
3.
所以 OnPaint()被呼叫一次之前 可能会呼叫OnEraseBkgnd()好几次
如果我们是一个在做图形化使用者介面的人,常会需要把一张美美的图片设为我们dialog的底图把绘图的代码放在OnPaint() 之中,可能会常碰到一些问题,比方说拖曳一个窗口在我们做的dialog上面一直移动,则dialog会变成灰色,直到动作停止才恢复,这是因为每次需要重绘的時候,程式都会马上呼叫OnEraseBkgnd()
这个问题的解法 比较差点的方法是把OnEraseBkgnd() 改写成不做事的function
如下所示
| BOOL CMyDlg::OnEraseBkgnd(CDC* pDC) { } |
以上本来是会呼叫CDialog::OnEraseBkgnd() 但是如果我们不呼叫的話
程式便不会画上灰色的底色了
比较好的做法是直接将绘图的程式从OnPaint()移到OnEraseBkgnd()来做
如下所示
|
// m_bmpBKGND 为一CBitmap对象 且事先早已载入我們的底图 // 底图的大小与我们的窗口client大小一致 BOOL CMyDlg::OnEraseBkgnd(CDC* pDC) { } |
特別要注意的是 取得重画大小是使用GetUpdateRect() 而不是GetClientRect(),如果使用GetClientRect() 会把不该重画的地方重画。
三、Invalidate*()与UpdateWindow
windows系统每隔一段时间就会发出一个WM_PAINT message,元件(如dialog,button,listbox.....等)在收到后便会检查自己有没有update region,如果有的话,便呼叫OnEraseBkgnd()以及OnPaint()来重绘元件。
知道了原理之后,我们便能对一些症状来对症下药:
Q: 我明明就改变了文字內容,可是窗口并没有马上秀出來,要我动一动窗口之后他才有反应
A: 使用Invalidate()给他一个update region这样程序便知道需要重画这个区域。
Q: 我对dialog做了五个改变 可是我只看到最后一个的结果,我希望可以看到这五个即时的改变
A: 每次做完改变后都要呼叫UpdateWindow()告诉程序马上重画,如有需要的话Invalidate()也要呼叫
前面提到两个function : Invalidate()跟UpdateWindow(),Invalidate()是把某个windows元件设为需要重绘的区域,在下次 WM_PAINT发出后,这个window元件才会重绘,注意Invalidate()并不会强制windows即时重画,UpdateWindow() 则是通知程序马上重画,除了Invalidate()以外,还有另外一个function叫做 InvalidateRect(),InvalidateRect()可以传入一个矩形,这个function的功用是让我们可以自行设定重画大小而不是 整个窗口
这个对控制重画程序效率是非常重要的,如果我们只是改变一小块,却要重画整个窗口,这是非常不划算的。
前面提到,我們可以自行设定重画大小,而不是整个窗口,因此在OnPaint()及OnEraseBkgnd()中,就必须要知道重画大小来进行重画,取得重画大小的function为 GetUpdateRect()如果update rect 只有一小块,而我们却把整个窗口给重画,这样便会盖掉不需要重画的地方,因此update region是要准确的控制才能正确的显示窗口。