MFC 双缓存绘图时移动系统控件导致重影如何解决?

sty_app 2019-05-06 08:35:26
正常来说双缓存绘图速度快,可以创建内存DC,先在内存DC上绘制好图形
然后绘制的时候直接使用Bitblt一次性将绘制好的DC复制到窗口DC上。

但是系统控件无法执行绘制到内存DC上去,只能将系统控件的区域,从在内存DC上的区域中,使用SelectClipRgn,强行挖除。

执行滚动操作时,由于绘制区域的变化,界面需要不断重绘,同时系统控件需要进行位置移动操作MoveWindow:


可以看到双缓存绘制图形的速度要比系统控件快很多,产生重影无法消除。
有人说MoveWindow的操作本身就比较慢:来源http://www.debugease.com/vc/1838830.html

要想不重影具体该怎么操作呢?
...全文
1722 19 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
千里送人头 2019-05-08
  • 打赏
  • 举报
回复
楼主,你这个场景,鼠标是一直按下不放的,往这个方向找一下原因看看能不能解决问题,之前我做的场景也有鼠标的操作,但场景是像上下左右的方向操作,会将鼠标释放
sty_app 2019-05-07
  • 打赏
  • 举报
回复
引用 1 楼 schlafenhamster 的回复:
"但是系统控件无法执行绘制到内存DC上去" 可以这样

case IDC_SCROLLBAR1:// "H bar"
m_hBar.SendMessage(WM_PAINT,(WPARAM)dcMem.m_hDC,0);
prompt="\"Horizontal\" scrollbar has been copied to clipbroad!";
break;


回答给的SendMessage给了关键的提示,一直以为MoveWindow需要使用UpdateWindow才可以刷新绘制,忘记了SendMessage和PostMessage是直接跳过消息队列的操作,提高了系统控件的绘制速度。避免了重影。
sty_app 2019-05-07
  • 打赏
  • 举报
回复

自绘对话框中添加的系统控件,闪烁以及重影问题基本解决。效果基本可以接受
关键代码如下:
在内存DC中绘制自绘控件的同时,将系统控件移动位置
BOOL CColorLightControl::InitControlEnd(SDrawControlShow *pDrawControlShow, CWnd *pWnd, CONTROL_INIT_STATUS initType)
{
if( initType==cis_status_dlg_init)
{
pDrawControlShow->dlgInitialized = TRUE;
return TRUE;
}
else if( initType==cis_status_control_init )
{
pDrawControlShow->controlInitialized = TRUE;
return TRUE;
}
if( !pDrawControlShow->dlgInitialized || !pDrawControlShow->controlInitialized)
return FALSE;

pDrawControlShow->SystemControls.RemoveAll();

int ClipRgnCount = 0;
for( int i=0; i<pDrawControlShow->IDCount; i++ ) //循环所有控件,定位所有系统控件
{
RECT boundRect;
BOOL bRes = FALSE;
if( m_CustomEdit.GetBoundControlPos(pDrawControlShow->pDrawControlInfo, i, &boundRect))
{
bRes = TRUE;
ClipRgnCount++;
}
else if(m_CustomControl.GetBoundControlPos(pDrawControlShow->pDrawControlInfo, i, &boundRect,pWnd))
{
bRes = TRUE;
ClipRgnCount++;
}
if(bRes)
{
//解析扩展部分
STranExpandInfo Temp;
CWnd *pCtrlWnd = pWnd->GetDlgItem(pDrawControlShow->pDrawControlInfo[i].boundID);
if(pCtrlWnd != NULL && IsWindow(pCtrlWnd->GetSafeHwnd()))
{
Temp.nID = i;
Temp.pWnd = pWnd;
Temp.pCtrlWnd = pCtrlWnd;
Temp.boundRect = boundRect;
Temp.pDrawControlInfo = &(pDrawControlShow->pDrawControlInfo[i]);
m_TranExpandManager.TranExpand(Temp);
//移动控件
pCtrlWnd->MoveWindow(&boundRect,FALSE); //系统控件移动位置,但不重绘
pDrawControlShow->SystemControls.Add(pCtrlWnd); //将系统控件加入需要重绘的控件集
if( pDrawControlShow->pDrawControlInfo[i].enable && pDrawControlShow->pDrawControlInfo[i].visible)
{
if( !pCtrlWnd->IsWindowVisible() /*|| initType!=cis_status_update*/)
pCtrlWnd->ShowWindow(SW_SHOWNORMAL);
}
else
{
if(pCtrlWnd->IsWindowVisible()/* || initType!=cis_status_update*/)
pCtrlWnd->ShowWindow(SW_HIDE);
}
}
}

}
CreateClipRgn(pDrawControlShow,pWnd);//取得屏幕背景刷新区域(将系统控件的区域从背景区域中挖除)
return TRUE;
}


循环绘制所有自绘控件
void CColorLightControl::DrawAllControl(SDrawControlShow *pDrawControlShow, CWnd *pWnd)
{
if( pDrawControlShow->memDC==NULL || pDrawControlShow->memDC.m_hDC==NULL)
return;
CSingleLock SingleLock(&m_CriticalSection);
if(SingleLock.IsLocked())
return;

CString strResPath;
strResPath.Format(_T("%s%s"), g_appPath, g_pCltcontrolstyle->GetResourceDir());
Graphics mGraphics(pDrawControlShow->memDC.m_hDC);
if(pDrawControlShow->pCusBackGroundColor != NULL)
mGraphics.Clear(RgbToArgb(255, *pDrawControlShow->pCusBackGroundColor));
else
mGraphics.Clear(RgbToArgb(255, g_pCltcontrolstyle->GetBackGroundColor()));
pDrawControlShow->memDC.SelectObject(&pDrawControlShow->textFont);
pDrawControlShow->memDC.SetBkMode(TRANSPARENT);

if(pDrawControlShow != NULL && pWnd != NULL)
{
HWND hwnd = pWnd->GetSafeHwnd();
if(pDrawControlShow->pFrontDraw != NULL)
pDrawControlShow->pFrontDraw(hwnd, &pDrawControlShow->memDC);
}

CDrawCustomControl *pControl = NULL;
for(int nID = 0; nID<pDrawControlShow->IDCount; nID++ )
{
pDrawControlShow->memDC.SetTextColor(g_pCltcontrolstyle->GetBackGroundColor());
if( !pDrawControlShow->pDrawControlInfo[nID].visible )
continue;
pControl = GetDlgControl(pDrawControlShow->pDrawControlInfo[nID].controlType);
if(pControl != NULL)
{
pControl->DrawControl(pDrawControlShow, nID, strResPath,pWnd,g_pCltcontrolstyle->GetBackGroundColor()); //在内存DC中绘制自定义控件
}
}

if(pDrawControlShow != NULL && pWnd != NULL)
{
HWND hwnd = pWnd->GetSafeHwnd();
if(pDrawControlShow->pBehindDraw != NULL)
pDrawControlShow->pBehindDraw(hwnd, &pDrawControlShow->memDC);
}

if( pWnd!=NULL)
pWnd->RedrawWindow(); //触发Onpaint,使用双缓存机制
}



在对话框的Onpaint中执行如下操作:
void CColorBaseDlg::OnPaint() 
{
CPaintDC dc(this); // device context for painting
if(!m_baseDlgSetInfo.m_memDCInit)
return ;
m_pIColorLightControl->RePaintSystemControl(&m_cltControlShow); //将需要重绘的系统控件绘制出来
dc.SelectClipRgn(&m_cltControlShow.clientRgn, RGN_COPY); //将挖掉系统控件区域的背景区域选到当前对话框的dc中
dc.BitBlt(m_cltControlShow.clientRect.left, m_cltControlShow.clientRect.top,m_cltControlShow.clientRect.Width(), m_cltControlShow.clientRect.Height(), &m_cltControlShow.memDC, 0, 0, SRCCOPY); //双缓存复制
}


RePaintSystemControl的实现:
void CColorLightControl::RePaintSystemControl( SDrawControlShow *pDrawControlShow )
{
if( !pDrawControlShow->dlgInitialized || !pDrawControlShow->controlInitialized)
return;

for(int i = 0; i < pDrawControlShow->SystemControls.GetCount(); i++)
pDrawControlShow->SystemControls.GetAt(i)->PostMessage(WM_PAINT,0,0); //使用PostMessage发送WM_PAINT消息绘制系统控件
}
sty_app 2019-05-07
  • 打赏
  • 举报
回复
引用 15 楼 Mr_sandman1994 的回复:
[quote=引用 14 楼 sty_app 的回复:]
[quote=引用 13 楼 Mr_sandman1994 的回复:]
直接在主界面上放控件通过movewindow不太好吧


一般都会弄一个子界面贴在主界面上,把这些控件都放子界面里面


软件大部分的控件都是自绘控件,只有edit、list、tree 这几个控件采用的是系统控件。[/quote]
自绘又不影响你放在子界面里[/quote]

界面也是自绘的。
绿色盒子 2019-05-07
  • 打赏
  • 举报
回复
引用 14 楼 sty_app 的回复:
[quote=引用 13 楼 Mr_sandman1994 的回复:]
直接在主界面上放控件通过movewindow不太好吧


一般都会弄一个子界面贴在主界面上,把这些控件都放子界面里面


软件大部分的控件都是自绘控件,只有edit、list、tree 这几个控件采用的是系统控件。[/quote]
自绘又不影响你放在子界面里
sty_app 2019-05-07
  • 打赏
  • 举报
回复
引用 13 楼 Mr_sandman1994 的回复:
直接在主界面上放控件通过movewindow不太好吧


一般都会弄一个子界面贴在主界面上,把这些控件都放子界面里面


软件大部分的控件都是自绘控件,只有edit、list、tree 这几个控件采用的是系统控件。
绿色盒子 2019-05-07
  • 打赏
  • 举报
回复
直接在主界面上放控件通过movewindow不太好吧


一般都会弄一个子界面贴在主界面上,把这些控件都放子界面里面
绿色盒子 2019-05-07
  • 打赏
  • 举报
回复
引用 9 楼 sty_app 的回复:
[quote=引用 8 楼 Mr_sandman1994 的回复:]
把iSet代码贴出来看看


大神,咋知道这软件叫iSet的呢。[/quote]
还知道colorlight呢
schlafenhamster 2019-05-06
  • 打赏
  • 举报
回复
控件内是这样

void cTree::OnPaint()
{
CPaintDC dc(this);
// get size
CRect rc;
GetClientRect(&rc);
// Create a compatible memory DC
CDC memDC;
memDC.CreateCompatibleDC(&dc);
// Select a compatible bitmap into the memory DC
CBitmap bitmap;
bitmap.CreateCompatibleBitmap( &dc, rc.Width(), rc.Height());
HBITMAP oldBmp=(HBITMAP)memDC.SelectObject(&bitmap);
// Let the control draws the tree.
DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0 );
// 'and' to the background,
dc.BitBlt(0,0,rc.Width(),rc.Height(),&memDC,0,0,SRCAND);
// set back
memDC.SelectObject(oldBmp);
// free
DeleteObject(bitmap.m_hObject);
DeleteObject(memDC);
}

注意
// Let the control draws the tree.
DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0 );
sty_app 2019-05-06
  • 打赏
  • 举报
回复
引用 3 楼 schlafenhamster 的回复:
控件的 OnPaint 人口 通常没有 DC , 其实 是可以 指定 DC 的 但 要用 SendMessage 


那什么时候发送消息呢? MoveWindow之后吗
schlafenhamster 2019-05-06
  • 打赏
  • 举报
回复
控件的 OnPaint 人口 通常没有 DC , 其实 是可以 指定 DC 的 但 要用 SendMessage 
sty_app 2019-05-06
  • 打赏
  • 举报
回复
引用 1 楼 schlafenhamster 的回复:
"但是系统控件无法执行绘制到内存DC上去" 可以这样

case IDC_SCROLLBAR1:// "H bar"
m_hBar.SendMessage(WM_PAINT,(WPARAM)dcMem.m_hDC,0);
prompt="\"Horizontal\" scrollbar has been copied to clipbroad!";
break;


这是个什么操作
schlafenhamster 2019-05-06
  • 打赏
  • 举报
回复
"但是系统控件无法执行绘制到内存DC上去" 可以这样

case IDC_SCROLLBAR1:// "H bar"
m_hBar.SendMessage(WM_PAINT,(WPARAM)dcMem.m_hDC,0);
prompt="\"Horizontal\" scrollbar has been copied to clipbroad!";
break;
sty_app 2019-05-06
  • 打赏
  • 举报
回复
附上 优化前的效果:
sty_app 2019-05-06
  • 打赏
  • 举报
回复
引用 5 楼 schlafenhamster 的回复:
控件内是这样

void cTree::OnPaint()
{
CPaintDC dc(this);
// get size
CRect rc;
GetClientRect(&rc);
// Create a compatible memory DC
CDC memDC;
memDC.CreateCompatibleDC(&dc);
// Select a compatible bitmap into the memory DC
CBitmap bitmap;
bitmap.CreateCompatibleBitmap( &dc, rc.Width(), rc.Height());
HBITMAP oldBmp=(HBITMAP)memDC.SelectObject(&bitmap);
// Let the control draws the tree.
DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0 );
// 'and' to the background,
dc.BitBlt(0,0,rc.Width(),rc.Height(),&memDC,0,0,SRCAND);
// set back
memDC.SelectObject(oldBmp);
// free
DeleteObject(bitmap.m_hObject);
DeleteObject(memDC);
}

注意
// Let the control draws the tree.
DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0 );



经过测试,这样对控件影响太大,我这边的操作改成 CEdit控件 MoveWindow的时候选择不重绘,然后直接给控件PostMessage WM_PAINT消息,让控件直接绘制,然后其他操作不变,效果成这个样子了:


效果勉强可以接受。
sty_app 2019-05-06
  • 打赏
  • 举报
回复
引用 8 楼 Mr_sandman1994 的回复:
把iSet代码贴出来看看


大神,咋知道这软件叫iSet的呢。
绿色盒子 2019-05-06
  • 打赏
  • 举报
回复
把iSet代码贴出来看看
伊航 2019-05-06
  • 打赏
  • 举报
回复
不是所有问题都能有完美解的,凑合能用就差不多了。不然就是你的思路走歪了。

15,980

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧