您的位置:首页 > 其它

MFC绘图窗口闪烁解决方法or内存DC

2016-05-11 13:33 525 查看



内存DC

在使用vc开发图形相关的应用程序时,常常需要使用MFC的CDC类直接把图形画在窗口上。这通常是通过响应windows的WM_PAINT消息实现的。如果要画的图形比较复杂,或者比较大,那么画图过程可能会造成窗口的闪烁。当窗口调整大小时,这种闪烁由为明显。

 

解决窗口闪烁问题的有效办法就是使用内存DC,也称为缓冲DC。在内存中准备一个和窗口DC相同属性的DC,在这个内存DC上执行画图操作。完成画图以后,把画图输出的内容整体复制到目标窗口DC上。因为画图操作不在窗口DC上进行,所以在画图的过程中窗口可以保持原来的内容。当画好的内容被复制到窗口DC时,因为复制操作执行的非常快,所以用户感觉窗口仿佛被立刻被画好,从而消除了从旧画面到白板再到新画面的闪烁现象。

 

生成内存DC主要用到以下四个函数:

 

CreateCompatibleDC(CDC* pDC )。CDC类的成员函数,用于创建一个和pDC指向的DC兼容的内存DC。

 

CreateDiscardableBITmap( CDC* pDC, int nWidth, int nHeight)。CBitmap类的成员函数,用于按指定尺寸创建一个和pDC指向的DC兼容的位图。    

 

SelectObject(CBitmap * pBitmap)。CDC类的成员函数,执行以后,所以在该DC上的图像输出都将被画到pBitmap指向的位图上。

 

BOOL BitBlt (int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )。CDC类的成员函数,用于从源DC(pSrcDC)复制一个矩形的图象到当前DC中。

   

对于一个窗口,我们可以用下面的代码来创建内存DC,在内存DC上输出,并最终复制到窗口DC上。

 


void PaintWnd(CWnd *<
4000
/span> pWnd)




...{


     CDC * pWndDC = pWnd->GetWindowDC();


     CRect WndRect = pWnd->GetWindowRect();


     


     CDC MemDC;


     CBitMap MemBitmap;


     


     MemDC.CreateCompatibleDC(pWndDC);         // 利用屏幕DC创建内存DC


     MemBitmap.CreateCompatibleBitmap(pWndDC,WndRect.Width(),WndRect.Height()); // 利用屏幕DC创建兼容的位图


 


     MemDC.SelectObject(&MemBitmap);            // 让内存DC输出到位图


 


     // 使用MemDC画图


     // 。。。。。。


     // 。。。。。。 
 |
// 。。。。。。 


    pWndDC->BitBlt(                                // 从内存DC复制到窗口DC


         0,0,


         WndRect.Width(),


         WndRect.Height(),


         &MemDC, 


         0,0,


         SRCCOPY);


}



 
当然,实际的情况下,我们需要考虑的更多,因为内存DC、位图的创建都可能会失败。为了简化代码,笔者定义了一个类CMemoryDC,包装了内存DC创建过程中的出错处理,内存DC的事后清理等操作,并自动复制内存DC的内容到目标DC上。

声明CMemoryDC类的头文件MemoryDC.h如下:

 


#pragma once


#include "Afxwin.h"


 


class CMemoryDC




...{


public:


     CMemoryDC(CDC *dc, RECT * rect,bool autoRender = false);


     ~CMemoryDC(void);


 


     bool IsOK();


     void Render(CDC * p_objectDC = NULL);


     CDC* GetMemoryDC();


     operator CDC * ();


private:


     bool m_bAutoRender;


     CRect m_DCRect;


     CDC* m_pOriginalDC;


     CDC m_MemoryDC;


     CBitmap m_MemoryBmp;


};



 

类的实现文件CMemoryDC.cpp如下:


#include ".MemoryDC.h"


 


CMemoryDC::CMemoryDC(CDC *dc, RECT * rect, bool autoRender)




...{    


     m_bAutoRender = autoRender;


     m_pOriginalDC = dc;


     if (dc==NULL || rect==NULL)


         return;


     if (!m_MemoryDC.CreateCompatibleDC(dc))


         return;


     m_DCRect.SetRect(rect->left, rect->top, rect->right, rect->bottom);   


     if (!m_MemoryBmp.CreateCompatibleBitmap(dc, m_DCRect.Width(), m_DCRect.Height()))


         return;


     m_MemoryDC.SelectObject(m_MemoryBmp);


}


 


CMemoryDC::~CMemoryDC(void)




...{


     if (m_bAutoRender)


         Render();


     if (m_MemoryDC.m_hDC!=NULL)


         m_MemoryDC.DeleteDC();


     if (m_MemoryBmp.m_hObject!=NULL)


         m_MemoryBmp.DeleteObject();


}


 


bool CMemoryDC::IsOK()




...{


     return m_MemoryDC.m_hDC!=NULL && m_MemoryBmp.m_hObject != NULL;


 


}


void CMemoryDC::Render(CDC * p_objectDC)




...{


     if (!IsOK())


         return;


 


     CDC * pDC = (p_objectDC==NULL ? m_pOriginalDC : p_objectDC);


     CSize Size = m_MemoryDC.GetViewportExt() ;


     pDC->BitBlt(


         m_DCRect.left, 


         m_DCRect.top,


         m_DCRect.Width(),


         m_DCRect.Height(),


         &m_MemoryDC, 


         0,0,


         SRCCOPY);


}


CDC* CMemoryDC::GetMemoryDC()




...{


     return & m_MemoryDC;


}


CMemoryDC::operator CDC * ()




...{


     return & m_MemoryDC;


}



使用这个类可以大大简化内存DC的创建操作。如果我们在窗口消息WM_PAINT的响应函数中使用内存DC,只要用如下这样简便的代码便可实现:


CRect Rect;


GetClientRect(Rect);


CPaintDC dc(this); // device context for painting


     CMemoryDC MemDC(&dc, Rect, true);


     if (MemDC.IsOK())




     ...{


         // 使用MemDC画窗口


}    


// MemDC析构时会自动把图像复制到dc,无需其它操作



使用CMemoryDC创建内存DC防止窗口闪烁,编程的代码和不使用内存DC时相比,数量和复杂性几乎没有增加。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: