MFC中怎样在改变大小的时候不闪烁?

peach_man 2011-10-18 10:42:19
我用一幅位图做背景,窗口上放一个CTabCtrl,然后在OnEraseBkgnd中绘背景。
背景和CTabCtrl颜色有反差,在改变大小的时候很闪烁,这个怎么解决?
我想的是在绘制的时候不绘CTabCtrl的部分,可是做不来,背景是一幅整的位图。
...全文
162 点赞 收藏 13
写回复
13 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
向立天 2011-11-30
您好
我是本版版主
此帖已多日无人关注
请您及时结帖
如您认为问题没有解决可按无满意结帖处理
另外本版设置了疑难问题汇总帖
并已在版面置顶
相关规定其帖子中有说明
您可以根据规定提交您帖子的链接
如您目前不想结帖只需回帖说明
我们会删除此结帖通知

见此回复三日内无回应
我们将强制结帖
相关规定详见界面界面版关于版主结帖工作的具体办法
回复
only_delusion 2011-11-01
额哦....
这种问题 有三点注意了 绝对没有任何闪烁

1.窗口create的时候 加上WS_clipchildren属性
2.OnEraseBkgnd 中 直接返回true 别在这里绘图。
3.onpaint中使用双缓冲 如果图简单 可以使用CMemmeryDC
{
CPaint dc(m_hWnd);
Crect _rc;
getclientRect(&_rc);
CMemeryDc _memdc(dc,_rc);
//将memdc 当成dc用就可以了。用后无需释放。
}
回复
allen_lanyuhai 2011-11-01
MemDC.BitBlt(0,m_nybk,wndRect.Width(),wndRect.Height()-m_nybk,&dcMemory,0,0,SRCCOPY);
这一句就是把控件区域绘制到内存DC上..
dc.BitBlt(0,0,wndRect.Width(),wndRect.Height(),&MemDC,0,0); 这个是把内存DC绘制到控件上
回复
peach_man 2011-10-31
都说双缓冲,可是怎么把控件区域绘制到内存DC上啊
回复
allen_lanyuhai 2011-10-20
用双缓存吧...其实可以这样子...
响应OnPaint()函数..
在该函数在中添加:
CPaintDC dc(this);
CRect wndRect;
GetClientRect(&wndRect);
CDC MemDC;
CBitmap MemBitmap;
MemDC.CreateCompatibleDC(&dc);
MemBitmap.CreateCompatibleBitmap(&dc,wndRect.Width(),wndRect.Heigth());
MemDC.SelectObject(&MemBitmap);
CBitmap btp,*pOldBitmap;
btp.LoadBitmap(IDB_BITMAPBK);
CDC dcMemory;
dcMemory.CreateCompatibleDC(&MemDC);
pOldBitmap=dcMemory.SelectObject(&btp);
MemDC.BitBlt(0,m_nybk,wndRect.Width(),wndRect.Height()-m_nybk,&dcMemory,0,0,SRCCOPY);
dcMemory.SelectObject(pOldBitmap);
dcMemory.DeleteDC();
dc.BitBlt(0,0,wndRect.Width(),wndRect.Height(),&MemDC,0,0);
回复
笨笨仔 2011-10-18
让背景与控件的交集不刷新
回复
wtbike 2011-10-18
可以试试双缓冲。
回复
向立天 2011-10-18
看一下这个函数
GetUpdateRect
在OnEraseBkgnd里值操作UpdateRect试试看
回复
S_S_Ge 2011-10-18
用双缓冲吧
回复
peach_man 2011-10-18
我上面贴得代码 没法用剪切区域啊?
双缓冲又怎么绘控件部分?
回复
Ronald_Z 2011-10-18
在VC中画图不闪,可以使用内存画图,然后将图片拷贝到设备上,但结果是加快图像显示速度,但还是很闪,显示图形如何避免闪烁,如何提高显示效率,而且多数人认为MFC的绘图函数效率很低,总是

想寻求其它的解决方案。MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
1、显示的图形为什么会闪烁?
我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint,而背景色

往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。
当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来绘制的图形进行清除,而又叠加上了新的图形。
有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。例如在OnDraw(CDC *pDC)中这样写:
pDC->MoveTo(0,0);
pDC->LineTo(100,100);
这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的时间与用背景清除屏幕所花的时间的比例越大

人对闪烁的感觉会越不明显。比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪烁5次;如果清除屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画只会闪烁一次。

这个也可以试验,在OnDraw(CDC *pDC)中这样写:
for(int i=0;i<100000;i++)
{
pDC->MoveTo(0,i);
pDC->LineTo(1000,i);
}
呵呵,程序有点变态,但是能说明问题。 说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要闪得

厉害一些,但是闪烁频率要低。
那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间的差异很小所以看起来不闪。如果不信,可

以在动画的每一帧中间加一张纯白的帧,不闪才怪呢。
2、如何避免闪烁
在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC提供的背景绘制过程了。实现的方法很多,
* 可以在窗口形成时给窗口的注册类的背景刷付NULL
* 也可以在形成以后修改背景
static CBrush brush(RGB(255,0,0));
SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
* 要简单也可在view类重载OnEraseBkgnd(CDC* pDC)直接返回TRUE
这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有图形进行显示以外,在内存中也有图形在绘

制。我们可以把要显示的图形先在内存中绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用

什么反差大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。
3、如何实现双缓冲
首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:
CDC MemDC; //首先定义一个显示设备对象
CBitmap MemBitmap;//定义一个位图对象
//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有地方画 ^_^
//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);
//将位图选入到内存显示设备中
//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);
//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));
//绘图
MemDC.MoveTo(……);
MemDC.LineTo(……);
//将内存中的图拷贝到屏幕上进行显示
pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);
//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();
上面的注释应该很详尽了,废话就不多说了。
4、如何提高绘图的效率
我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过

程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。
实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身

为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大

多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时

得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉

在裁剪区外的绘图过程了。可以先用pDC->GetClipBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高
回复
peach_man 2011-10-18
双缓冲 怎么绘控件的部分呢?
回复
peach_man 2011-10-18
怎么做啊
我在OnEraseBkgnd中
CRect wndRect;
GetClientRect(&wndRect);
CBitmap btp,*pOldBitmap;
btp.LoadBitmap(IDB_BITMAPBK);
CDC dcMemory;
dcMemory.CreateCompatibleDC(pDC);
pOldBitmap=dcMemory.SelectObject(&btp);
pDC->BitBlt(0,m_nybk,wndRect.Width(),wndRect.Height()-m_nybk,&dcMemory,0,0,SRCCOPY);
dcMemory.SelectObject(pOldBitmap);
dcMemory.DeleteDC();
return TRUE;
回复
相关推荐
发帖
界面
创建于2007-09-28

1.5w+

社区成员

VC/MFC 界面
申请成为版主
帖子事件
创建了帖子
2011-10-18 10:42
社区公告
暂无公告