现在的位置: 首页 > 综合 > 正文

VC防止窗口及其控件(如CListCtrl)闪烁的简单方法(一组有用的宏)

2013年03月23日 ⁄ 综合 ⁄ 共 3666字 ⁄ 字号 评论关闭
        本文主要是通过网上一篇文章提供的解决办法,整理了一组宏来实现防闪烁的。本文的重点是展示这个宏及其使用方法。 解决方法的原理请参考: http://www.cndes.cn/Designs/Program/200608052716.html

 针对上文中提到的 “有效的进行搽除" 方法整理如下:
        原文作者的思路是复写WM_ERASEBKGND消息处理函数
OnEraseBkgnd,
 只对空白区域使用指定背景色重绘。而对已经有控件的区域,不做重绘,从而
 避免窗口及其控件闪烁。

      上述方法确实有效,但在有很多控件的情况下,计算一个窗口中需要擦除并重绘的”空白区域“
是一件很麻烦的事情。为了方便这种方法的实际应用,我写了一组宏来完成”计算空白区域“的功能:
/***********************************************************************************************************/
/************************************************************************/
/* 宏功能: 界面刷新时仅刷新指定控件以外的空白区域;可有效避免窗口闪烁
/* 使用于: WM_ERASEBKGND 消息处理函数 OnEraseBkgnd();
/************************************************************************/
#define ERASE_BKGND_BEGIN /
    CRect bgRect;/
    GetWindowRect(&bgRect);/
    CRgn bgRgn;/
    bgRgn.CreateRectRgnIndirect(bgRect);
//#define ERASE_BKGND_BEGIN

// Marco parameter 'IDC' specifies the identifier of the control
#define ADD_NOERASE_CONTROL(IDC)/
{/
    CRect controlRect;/
    GetDlgItem(IDC)->GetWindowRect(&controlRect);/
    CRgn controlRgn;/
    controlRgn.CreateRectRgnIndirect(controlRect);/
    if(bgRgn.CombineRgn(&bgRgn, &controlRgn, RGN_XOR)==ERROR)/
        return false;/
}

// Marco parameter 'noEraseRect' specifies a screen coordinates based RECT,
// which needn't erase.
#define ADD_NOERASE_RECT(noEraseRect)/
{/
    CRgn noEraseRgn;/
    noEraseRgn.CreateRectRgnIndirect(noEraseRect);/
    if(bgRgn.CombineRgn(&bgRgn, &noEraseRgn, RGN_XOR)==ERROR)/
        return false;/
}

// Marco parameter 'pDC' is a kind of (CDC *) type.
// Marco parameter 'clBrushColor' specifies the color to brush the area.
#define ERASE_BKGND_END(pDC, clBrushColor)/
    CBrush brush;/
    brush.CreateSolidBrush(clBrushColor);/
    CPoint saveOrg = (pDC)->GetWindowOrg();/
    (pDC)->SetWindowOrg(bgRect.TopLeft());/
    (pDC)->FillRgn(&bgRgn, &brush);/
    (pDC)->SetWindowOrg(saveOrg);/
    brush.DeleteObject();/
//#define ERASE_BKGND_END

/***********************************************************************************************************/
说明:
1)宏
ERASE_BKGND_BEGIN  和   ERASE_BKGND_END(pDC, clBrushColor) 搭配使用。
2)宏
ADD_NOERASE_CONTROL(IDC) ADD_NOERASE_RECT(noEraseRect) 根据需要放在上面两个宏的中间,用来添加不需要重绘背景的区域(正是这些区域导致了闪烁),使用次数不限。其中参数noEraseRect是一个屏幕坐标系的RECT类型或CRect类型。

使用举例1:
在当前窗体的类中重写
WM_ERASEBKGND消息处理函数如下:
BOOL CMyWnd::OnEraseBkgnd(CDC* pDC)
{
    ERASE_BKGND_BEGIN;
    ADD_NOERASE_RGN(IDC_BUTTON2);
    ADD_NOERASE_RGN(IDC_BUTTON1);
    ADD_NOERASE_RGN(IDC_LIST_STAT);
    ERASE_BKGND_END(pDC, GetSysColor(COLOR_3DFACE));
    return false;
}
上面的
IDC_BUTTON2,IDC_BUTTON1,IDC_LIST_STAT即窗体上的控件。
 你可以指定其他已存在的控件。
 这样,窗口在擦除背景时,将只对上述控件以后的”空白区域“使用系统色重绘,有效避免了闪烁。

备注:
    重载WM_ERASEBKGND消息处理函数OnEraseBkgnd的方法,
     选择View->ClassWizard->classinfo选项卡:message filter下拉框:
     选择window,然后再选择message maps选项卡,在messages下拉框应该可以找到
     wm_erasebkgnd.双击添加.

使用举例2:防止CListCtrl在拉动窗口时闪烁。
/*
* No further full-erasing is required,
* to prevent screen flashing caused by background erase and view repaint.
* Only erase the blank area.
*/
BOOL CExListCtrl::OnEraseBkgnd(CDC* pDC)
{
//compute the holding-data-items area of this list control
CRect rect;
CPoint dataRgnTopLeftPoint;
CPoint dataRgnBottomRightPoint;
GetItemPosition(0 , &dataRgnTopLeftPoint);
GetItemPosition(GetItemCount() , &dataRgnBottomRightPoint);
if(!GetHeaderCtrl()->GetItemRect(GetHeaderCtrl()->GetItemCount()-1, rect))
return CListCtrl::OnEraseBkgnd(pDC);
dataRgnBottomRightPoint.x = rect.right;

rect.SetRect(dataRgnTopLeftPoint, (CPoint)(dataRgnBottomRightPoint - CPoint(2,2)));
ClientToScreen(dataRgnRect);

//compute and erase the blank area. Using the Marco.
ERASE_BKGND_BEGIN;
ADD_NOERASE_RECT(dataRgnRect);
ERASE_BKGND_END(pDC, GetBkColor());

return false;
}

说明:CListCtrl在拉动的时候,会前以背景色重刷背景,再在上面绘制有数据的Items,
      而没有数据的区域则保持背景色。因此,如果在
BOOL CExListCtrl::OnEraseBkgnd(CDC* pDC)
      函数中简单的return false,那么没有数据的区域将显示不正常。
          故举例2中先计算出有数据的items的区域,这是不需要以背景重刷的
区域
      再使用本文的宏,就可以有效避免CListCtrl在拉动时候的闪烁。
 

抱歉!评论已关闭.