CListBox自绘的具体步骤就不再详述了,不过还是要留意以下几点: LBS_OWNERDRAWVARIABLE:允许每一项有不同的长度。要想改变某一项的高度,通过MeasureItem无法做到(宽度可以)。需要用到函数SetItemHeight。 LBS_NOTIFY:只有加入该风格,CListBox才能响应双击等消息。 DrawItem:核心自绘函数。不过并不建议直接使用CListBox的DC来绘制,而是采用双缓冲的方式,可以有效的在Invalidate(FALSE)时减小刷新。 本文主要介绍实际应用中的一种情况。有些时候,当双击CListBox某一项的时候,我们希望它能展开,显示更多的信息;再次双击一下,能够收缩,回到正常状态。功能的具体实现很简单,只需要调用SetItemHeight实时调整大小,在DrawItem判断该项是否为展开项来进行显示。
但是,在刷新显示的时候碰到一个问题。当双击收缩最后一项时,如果调用Invalidate(TRUE),自然会产生轻微的闪烁;本文采用了Invalidate(FALSE)来避免闪烁,却发现最后一项收缩后显示正常,但是刚才扩展的区域,因为未刷新,仍然显示刚才的内容,这种效果显然是不正确的。造成的主要原因是因为DrawItem只会负责绘制该项的区域,如果不实时改变每项的高度,这已经足够了;但是由于每项高度在双击后会改变,因此其有义务维护上一次该项的绘制区域。
因此一个技巧就是在绘制最后一项时,判断其是否因为收缩而需要刷新,如果是,则利用双缓冲绘制时,需要绘制从该项往下整个客户区(或者展开区域);如果不是,则绘制其实时区域。
核心代码如下:
// 如果不是最后一项 绘制其实时区域(展开或者收缩)
if(lpDrawItemStruct->itemID != GetCount() - 1)
pDC->BitBlt(wholeRect.left,wholeRect.top,wholeRect.Width(),wholeRect.Height(),&memDC,0,0,SRCCOPY);
else
{
//收缩后刷新,不能只绘制自己项的部分,而是客户区朝下
if(m_bInvalidated)
{
pDC->BitBlt(wholeRect.left,wholeRect.top,wholeRect.Width(),clientrect.Height(),&memDC,0,0,SRCCOPY);
m_bInvalidated = FALSE;
}
else
pDC->BitBlt(wholeRect.left,wholeRect.top,wholeRect.Width(),wholeRect.Height(),&memDC,0,0,SRCCOPY);
}
评论