解決這一問題的辦法依然是使用雙緩沖,和上次不同的上,用於緩沖的內存DC和位圖要保持住,而非隨用隨建。為了解決第一點不足,需要建立一個內存DC和位圖,並在OnDraw過程中將其拷貝到前台。這樣歐文們作圖的時候將圖做在內存DC上,然后使窗口刷新,便看到了所做的圖了。當窗體移動或被遮擋時,窗口需要重繪,只是把我們的緩沖重新繪制一遍,因此已經畫好的線不會被擦除。解決第二的問題的方法主要有兩種,一種是設置畫筆的模式為異或模式,這樣的話在同意位置畫兩次的話等同於什么都沒畫。但是這樣會犧牲線條本身的顏色屬性,如果背景是單一的顏色,自然是看不出來的,但是如果背景顏色豐富,那我們的線條也就隨着多姿多彩了;第二種方法是比較好用的臟矩形法,臟矩形法的主要內容就是每次畫面的刷新只更新需要更新的那一塊區域,這正是Flash采用的方式,效率比較高。在本程序中為簡單起見,沒有采用完全的臟矩形法,臟矩形始終定義為整個客戶區大小。為此,需要重新定義一個新的緩沖區,用來保存即將變臟的矩形,以備之后恢復所用。在響應WM_MOUSEMOVE消息的過程中,先將新緩沖區上的圖形還原到舊緩沖區上,這樣可以遮擋住上次的線條。再在舊緩沖區上作圖,然后刷新窗口,我們便看到了動態效果。不知道這是不是傳說中的三緩沖技術呢?
以下是代碼的分析:
CBitmap memBakBMP; //新緩沖區內存DC CDC memBakDC; //新緩沖區用的位圖 BOOL bClicked; //判斷是否應當作圖 CPoint ptBegin; //記錄圖象開始的位置 CBitmap memBMP; //新緩沖區用的位圖 CDC memDC; //舊緩沖區內存DC
響應WM_CREATE消息的時候做初始化工作:
int CNSSDrawerView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; memDC.CreateCompatibleDC(NULL); memBMP.CreateCompatibleBitmap(&memDC,1024,768); memDC.SelectObject(&memBMP); memDC.FillSolidRect(0,0,1024,768,GetDC()->GetBkColor()); memBakDC.CreateCompatibleDC(NULL); memBakBMP.CreateCompatibleBitmap(&memBakDC,1024,768); memBakDC.SelectObject(&memBakBMP); return 0; }
相應的,在響應WM_DESTROY消息的時候,也要做善后工作:
void CNSSDrawerView::OnDestroy() { CView::OnDestroy(); memDC.DeleteDC(); memBMP.DeleteObject(); memBakDC.DeleteDC(); memBakBMP.DeleteObject(); }
干預WM_ERASEBKGND消息的響應,這里是最容易忽略的地方,要特別注意
BOOL CNSSDrawerView::OnEraseBkgnd(CDC* pDC) { return TRUE; //CView::OnEraseBkgnd(pDC); }
部分變量在構造函數中初始化:
CNSSDrawerView::CNSSDrawerView() { ptBegin = 0; bClicked = FALSE; }
響應WM_LBUTTONDOWN消息,着是畫線的開始:
void CNSSDrawerView::OnLButtonDown(UINT nFlags, CPoint point) { ptBegin = point; bClicked = TRUE; CRect rect; GetClientRect(&rect); memBakDC.BitBlt(0,0,rect.Width(),rect.Height(),&memDC,0,0,SRCCOPY); CView::OnLButtonDown(nFlags, point); }
相應的,WM_LBUTTONUP消息的響應是畫線的結束:
void CNSSDrawerView::OnLButtonUp(UINT nFlags, CPoint point) { bClicked = FALSE; CView::OnLButtonUp(nFlags, point); }
最主要的部分在響應WM_MOUSEMOVE消息的模塊中:
void CNSSDrawerView::OnMouseMove(UINT nFlags, CPoint point) { if(bClicked) { CRect rect; GetClientRect(&rect); memDC.BitBlt(0,0,rect.Width(),rect.Height(),&memBakDC,0,0,SRCCOPY); memDC.MoveTo(ptBegin); memDC.LineTo(point); Invalidate(); } CView::OnMouseMove(nFlags, point); }