界面大小变化时候刷新闪烁问题

时间:2022-02-21 14:59:38
一个背景图片,上面放上一些自定义类的按钮,当窗体大小变化时候按钮位置总是闪烁背景图片,修改背景图片把按钮位置挖下来后(用PS),程序中指定挖下来区域的颜色(白色)为透明色,再把按钮填到被挖下来的位置,这么做拉动窗体时闪烁现象变轻了,但有黑色(也看不太清)闪现。有什么办法让窗体拉动的时候不闪烁哪?我看QQ那界面拉动的时候控件和背景图片就不闪烁不知道是怎么实现的,那位高人给指点一下。

21 个解决方案

#1


这和你的绘图时机,绘图区域大小等都有关系,你这么说很难说怎样才能不闪烁。但我的方法是,在WM_PAIT消息里,获取需重绘区域的大小,然后在我的缓冲将像素数据拷贝到目标DC中,没有出现你说闪烁现像。

#2


你可以试下把擦除背景的函数修改下,
让它直接return FALSE就会好。
因为擦除背景函数会在重绘时默认把dc至为白色

#3


引用 1 楼 Michael_g 的回复:
这和你的绘图时机,绘图区域大小等都有关系,你这么说很难说怎样才能不闪烁。但我的方法是,在WM_PAIT消息里,获取需重绘区域的大小,然后在我的缓冲将像素数据拷贝到目标DC中,没有出现你说闪烁现像。


你说的是这个意思吧
一下是一些背景图片的摆放。。
void CxxxDlg::OnPaint() 
{
if (IsIconic())
{
...
}
else
{
CPaintDC dc(this);
        dc.SetStretchBltMode(COLORONCOLOR);
        CBitmap   bitmap;
CBitmap*   pOldBitmap;   
CDC   MemDC;   
RECT   rect;   
        BITMAP   bmp;
        GetClientRect(&rect); 
        MemDC.CreateCompatibleDC(&dc);
bitmap.LoadBitmap(IDB_SPEED);     
pOldBitmap=MemDC.SelectObject(&bitmap);       
bitmap.GetBitmap(&bmp);
dc.StretchBlt(131,0,rect.right-121-131,57,&MemDC,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);   
MemDC.SelectObject(pOldBitmap);
        bitmap.DeleteObject();
        MemDC.DeleteDC();

        MemDC.CreateCompatibleDC(&dc);
bitmap.LoadBitmap(IDB_xxx);     
...
        MemDC.DeleteDC();

        MemDC.CreateCompatibleDC(&dc);
bitmap.LoadBitmap(IDB_xxx);     
...
        MemDC.DeleteDC();

}

一下一是一些按钮(继承CBtton类)的摆放
void CPlayerDlg::OnSize(UINT nType, int cx, int cy) 
{
CDialog::OnSize(nType, cx, cy);
...
rc_save = CPoint(80,4) + CRect(0,0,39,50);
GetDlgItem(IDC_BUTTONXX)->MoveWindow(&rc_save);
...
}



#4


引用 2 楼 bofrobber 的回复:
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色

BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 
{
return FALSE;
}
是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办?

#5


漏了响应WM_ERASEBKGND消息吧.

#6


引用 4 楼 pasdtniuren 的回复:
引用 2 楼 bofrobber 的回复:
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色 
 
BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

return FALSE; 

是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办?


return TRUE..

#7


放按钮的父窗口再加一个WS_CLIPCHILDREN 属性

#8


绘制时用双DC,然后再BitBlt内存DC到窗口DC上,OnEraseBkgnd返回True,不然系统帮你重绘背景

#9


引用 6 楼 lambochan 的回复:
引用 4 楼 pasdtniuren 的回复:
引用 2 楼 bofrobber 的回复: 
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色 

BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

return FALSE; 

是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办? 
 

return TRUE..

------
补充下,确实是return true, 但是记得要层层传递呀, 有一个父(或以上的)窗口没处理这个就,没什么效果的。

#10


在Windows 下,窗口重绘时,会首先会窗口背景色,然后是onPaint事件绘制窗口内容.我看了一下你的贴出的代码,里面的操作太多,这样的话当窗口重绘时,先画完背景,然后好长时间后在画图像当然会闪烁(即使不响应背景重绘事件也不行,这样会在屏幕上留下图像的碎片),解决方法是把你的所有要载入的图片,全部载入到全局的DC中,这样在OnPaint中需要 StretchBlt()这个方法就行了,应该会解决这个问题。还有重绘时,你要调用 GetUpdateRect 函数,来获取一个更新区域,有时你的窗体并不需要全部重画,这样可以加快绘制速度。

#11


引用 9 楼 spring203 的回复:
引用 6 楼 lambochan 的回复:
引用 4 楼 pasdtniuren 的回复: 
引用 2 楼 bofrobber 的回复: 
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色 

BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

return FALSE; 

是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办? 


return TRUE.. 
 
------ 
补充下,确实是return tr…

层层传递是每个窗体都相应OnEraseBkgnd方法的意思吧,这么做了没什么改进啊

#12


引用 10 楼 Michael_g 的回复:
在Windows 下,窗口重绘时,会首先会窗口背景色,然后是onPaint事件绘制窗口内容.我看了一下你的贴出的代码,里面的操作太多,这样的话当窗口重绘时,先画完背景,然后好长时间后在画图像当然会闪烁(即使不响应背景重绘事件也不行,这样会在屏幕上留下图像的碎片),解决方法是把你的所有要载入的图片,全部载入到全局的DC中,这样在OnPaint中需要 StretchBlt()这个方法就行了,应该会解决这个问题。还有重绘时,你要调用 GetUp…

有道理,那么怎么载入到全局的调查中那,能说的详细点?谢谢

#13


很简单,将你的图片在程序初始代是就载入,然后建立几个全局的CDC类型变量,将图片载入这些DC就行了.

#14


1 ,  父窗口加WS_CLIPCHILDREN ,

2  , 
void CPlayerDlg::OnSize(UINT nType, int cx, int cy) 
{
CDialog::OnSize(nType, cx, cy);
...
rc_save = CPoint(80,4) + CRect(0,0,39,50);
GetDlgItem(IDC_BUTTONXX)->MoveWindow(&rc_save);
GetDlgItem(IDC_BUTTONXX)->Invalidate(FALSE);
...
}

#15


用双缓冲画图能够将图像画图一次性画好,但是应该还是不能解决你的问题,
你的问题似乎是重绘的时候把子窗口那部分图也重绘了
你可以在擦除背景函数做这样处理
BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

//假设一个button,
CRect rect;
GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rect);
ScreenToClient(&rect);
pDC->ExcludeClipRect (&rect);//该函数可以使这部分区域不被画,你可以再试试看下
CDialog::OnEraseBkgnd(pDC); 

最后建议你使用双缓冲画图,这样一次性画完

#16


定义一个成员变量 CBrush m_brush;
在OnInitDialog初始化,在OnCtlColor返回就可以了

BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CBitmap bmp;
bmp.加载你的背景图片
        m_brush.CreatePatternBrush(&bmp);

}
HBRUSH Cldlg1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO:  在此更改 DC 的任何属性

// TODO:  如果默认的不是所需画笔,则返回另一个画笔
return (HBRUSH )(m_brush);
}

#17


定义一个成员变量 CBrush m_brush;
在OnInitDialog初始化,在OnCtlColor返回就可以了

BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CBitmap bmp;
bmp.加载你的背景图片
        m_brush.CreatePatternBrush(&bmp);

}
HBRUSH Cldlg1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO:  在此更改 DC 的任何属性

// TODO:  如果默认的不是所需画笔,则返回另一个画笔
return (HBRUSH )(m_brush);
}

#18


不好意思,网络太慢,不小心发了两次

#19


把窗口重绘的代码放在OnPaint里面,而不是什么时候想绘就绘。另外在项目的代码中找找InvalidateRect这个函数, 把擦除背景(是最后一个参数)设为FALSE.

#20


引用 15 楼 bofrobber 的回复:
用双缓冲画图能够将图像画图一次性画好,但是应该还是不能解决你的问题, 
你的问题似乎是重绘的时候把子窗口那部分图也重绘了 
你可以在擦除背景函数做这样处理 
BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

//假设一个button, 
CRect rect; 
GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rect); 
ScreenToClient(&rect); 
pDC->ExcludeClipRect (&rect);//该函数可以使这部分区域不被画,你可以再试试看下 
CDialog:…

我把上面代码放在onpaint函数里了,问题解决了,嘎嘎

ExcludeClipRect 这个函数很好很强大,灵活使用可以解决很多刷新闪烁问题,还有个问题就是为什么指定区域不刷新了,我拖拽窗口时候那些区域并没有出现问题(按钮也移到那个位置了)?当然不出问题正是我想要的,我只是不明白为什么,给我讲讲呗,谢谢!

#21


该回复于2011-01-12 10:58:24被版主删除

#1


这和你的绘图时机,绘图区域大小等都有关系,你这么说很难说怎样才能不闪烁。但我的方法是,在WM_PAIT消息里,获取需重绘区域的大小,然后在我的缓冲将像素数据拷贝到目标DC中,没有出现你说闪烁现像。

#2


你可以试下把擦除背景的函数修改下,
让它直接return FALSE就会好。
因为擦除背景函数会在重绘时默认把dc至为白色

#3


引用 1 楼 Michael_g 的回复:
这和你的绘图时机,绘图区域大小等都有关系,你这么说很难说怎样才能不闪烁。但我的方法是,在WM_PAIT消息里,获取需重绘区域的大小,然后在我的缓冲将像素数据拷贝到目标DC中,没有出现你说闪烁现像。


你说的是这个意思吧
一下是一些背景图片的摆放。。
void CxxxDlg::OnPaint() 
{
if (IsIconic())
{
...
}
else
{
CPaintDC dc(this);
        dc.SetStretchBltMode(COLORONCOLOR);
        CBitmap   bitmap;
CBitmap*   pOldBitmap;   
CDC   MemDC;   
RECT   rect;   
        BITMAP   bmp;
        GetClientRect(&rect); 
        MemDC.CreateCompatibleDC(&dc);
bitmap.LoadBitmap(IDB_SPEED);     
pOldBitmap=MemDC.SelectObject(&bitmap);       
bitmap.GetBitmap(&bmp);
dc.StretchBlt(131,0,rect.right-121-131,57,&MemDC,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);   
MemDC.SelectObject(pOldBitmap);
        bitmap.DeleteObject();
        MemDC.DeleteDC();

        MemDC.CreateCompatibleDC(&dc);
bitmap.LoadBitmap(IDB_xxx);     
...
        MemDC.DeleteDC();

        MemDC.CreateCompatibleDC(&dc);
bitmap.LoadBitmap(IDB_xxx);     
...
        MemDC.DeleteDC();

}

一下一是一些按钮(继承CBtton类)的摆放
void CPlayerDlg::OnSize(UINT nType, int cx, int cy) 
{
CDialog::OnSize(nType, cx, cy);
...
rc_save = CPoint(80,4) + CRect(0,0,39,50);
GetDlgItem(IDC_BUTTONXX)->MoveWindow(&rc_save);
...
}



#4


引用 2 楼 bofrobber 的回复:
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色

BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 
{
return FALSE;
}
是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办?

#5


漏了响应WM_ERASEBKGND消息吧.

#6


引用 4 楼 pasdtniuren 的回复:
引用 2 楼 bofrobber 的回复:
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色 
 
BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

return FALSE; 

是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办?


return TRUE..

#7


放按钮的父窗口再加一个WS_CLIPCHILDREN 属性

#8


绘制时用双DC,然后再BitBlt内存DC到窗口DC上,OnEraseBkgnd返回True,不然系统帮你重绘背景

#9


引用 6 楼 lambochan 的回复:
引用 4 楼 pasdtniuren 的回复:
引用 2 楼 bofrobber 的回复: 
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色 

BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

return FALSE; 

是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办? 
 

return TRUE..

------
补充下,确实是return true, 但是记得要层层传递呀, 有一个父(或以上的)窗口没处理这个就,没什么效果的。

#10


在Windows 下,窗口重绘时,会首先会窗口背景色,然后是onPaint事件绘制窗口内容.我看了一下你的贴出的代码,里面的操作太多,这样的话当窗口重绘时,先画完背景,然后好长时间后在画图像当然会闪烁(即使不响应背景重绘事件也不行,这样会在屏幕上留下图像的碎片),解决方法是把你的所有要载入的图片,全部载入到全局的DC中,这样在OnPaint中需要 StretchBlt()这个方法就行了,应该会解决这个问题。还有重绘时,你要调用 GetUpdateRect 函数,来获取一个更新区域,有时你的窗体并不需要全部重画,这样可以加快绘制速度。

#11


引用 9 楼 spring203 的回复:
引用 6 楼 lambochan 的回复:
引用 4 楼 pasdtniuren 的回复: 
引用 2 楼 bofrobber 的回复: 
你可以试下把擦除背景的函数修改下, 
让它直接return FALSE就会好。 
因为擦除背景函数会在重绘时默认把dc至为白色 

BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

return FALSE; 

是这个意思吧,我以前已经这么做了,这么做确实会好些但还是有,怎么办? 


return TRUE.. 
 
------ 
补充下,确实是return tr…

层层传递是每个窗体都相应OnEraseBkgnd方法的意思吧,这么做了没什么改进啊

#12


引用 10 楼 Michael_g 的回复:
在Windows 下,窗口重绘时,会首先会窗口背景色,然后是onPaint事件绘制窗口内容.我看了一下你的贴出的代码,里面的操作太多,这样的话当窗口重绘时,先画完背景,然后好长时间后在画图像当然会闪烁(即使不响应背景重绘事件也不行,这样会在屏幕上留下图像的碎片),解决方法是把你的所有要载入的图片,全部载入到全局的DC中,这样在OnPaint中需要 StretchBlt()这个方法就行了,应该会解决这个问题。还有重绘时,你要调用 GetUp…

有道理,那么怎么载入到全局的调查中那,能说的详细点?谢谢

#13


很简单,将你的图片在程序初始代是就载入,然后建立几个全局的CDC类型变量,将图片载入这些DC就行了.

#14


1 ,  父窗口加WS_CLIPCHILDREN ,

2  , 
void CPlayerDlg::OnSize(UINT nType, int cx, int cy) 
{
CDialog::OnSize(nType, cx, cy);
...
rc_save = CPoint(80,4) + CRect(0,0,39,50);
GetDlgItem(IDC_BUTTONXX)->MoveWindow(&rc_save);
GetDlgItem(IDC_BUTTONXX)->Invalidate(FALSE);
...
}

#15


用双缓冲画图能够将图像画图一次性画好,但是应该还是不能解决你的问题,
你的问题似乎是重绘的时候把子窗口那部分图也重绘了
你可以在擦除背景函数做这样处理
BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

//假设一个button,
CRect rect;
GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rect);
ScreenToClient(&rect);
pDC->ExcludeClipRect (&rect);//该函数可以使这部分区域不被画,你可以再试试看下
CDialog::OnEraseBkgnd(pDC); 

最后建议你使用双缓冲画图,这样一次性画完

#16


定义一个成员变量 CBrush m_brush;
在OnInitDialog初始化,在OnCtlColor返回就可以了

BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CBitmap bmp;
bmp.加载你的背景图片
        m_brush.CreatePatternBrush(&bmp);

}
HBRUSH Cldlg1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO:  在此更改 DC 的任何属性

// TODO:  如果默认的不是所需画笔,则返回另一个画笔
return (HBRUSH )(m_brush);
}

#17


定义一个成员变量 CBrush m_brush;
在OnInitDialog初始化,在OnCtlColor返回就可以了

BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CBitmap bmp;
bmp.加载你的背景图片
        m_brush.CreatePatternBrush(&bmp);

}
HBRUSH Cldlg1Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

// TODO:  在此更改 DC 的任何属性

// TODO:  如果默认的不是所需画笔,则返回另一个画笔
return (HBRUSH )(m_brush);
}

#18


不好意思,网络太慢,不小心发了两次

#19


把窗口重绘的代码放在OnPaint里面,而不是什么时候想绘就绘。另外在项目的代码中找找InvalidateRect这个函数, 把擦除背景(是最后一个参数)设为FALSE.

#20


引用 15 楼 bofrobber 的回复:
用双缓冲画图能够将图像画图一次性画好,但是应该还是不能解决你的问题, 
你的问题似乎是重绘的时候把子窗口那部分图也重绘了 
你可以在擦除背景函数做这样处理 
BOOL CPlayerDlg::OnEraseBkgnd(CDC* pDC) 

//假设一个button, 
CRect rect; 
GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rect); 
ScreenToClient(&rect); 
pDC->ExcludeClipRect (&rect);//该函数可以使这部分区域不被画,你可以再试试看下 
CDialog:…

我把上面代码放在onpaint函数里了,问题解决了,嘎嘎

ExcludeClipRect 这个函数很好很强大,灵活使用可以解决很多刷新闪烁问题,还有个问题就是为什么指定区域不刷新了,我拖拽窗口时候那些区域并没有出现问题(按钮也移到那个位置了)?当然不出问题正是我想要的,我只是不明白为什么,给我讲讲呗,谢谢!

#21


该回复于2011-01-12 10:58:24被版主删除