nsnow

一、CPaintDC与CClientDC

CPaintDC是从CDC派生出来的。在构造时自动调用CWnd::BeginPaint,析构时调用CWnd::EndPaint。通常CPaintDC用来响应WM_PAINT消息。一般应用在OnPaint函数.   
CClientDC也是从CDC派生出来的.构造时自动调用GetDC函数,析构时自动调用ReleaseDC函数.一般应用于客户区窗口的绘制.   另外, CClientDC是在对画客户区时要用到的内存设备环境CPaintDC用于有重画消息发出时才使用的内存设备环境。

 

 

二、OnEraseBkgnd与OnPaint

在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*()与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是要准确的控制才能正确的显示窗口。

分类:

技术点:

相关文章: