如何修复ListView左上角自定义复选框的滚动残影模糊问题
问题根因
你现在遇到的滚动残影,根源是把控件直接加在ListView的控件集合里时,和ListView的滚动绘制机制不兼容:ListView处理滚动时会直接把原有客户区的位图做块平移实现快速滚动,根本不会单独擦除叠在上面的CheckBox旧位置的像素——这些残留的像素不会被任何重绘逻辑覆盖,最后就形成了模糊拖影。
可落地解决办法
方案1:自绘复选框(性能最好、无残影,优先选)
完全不用独立的CheckBox控件,把复选框的绘制和ListView本身的重绘流程绑定,从根源消除重绘不同步的问题:
- 删掉所有把CheckBox加到ListView子控件集合的代码,移除独立的CheckBox实例
- 写个继承自ListView的自定义控件类,在构造函数里开启双缓冲,减少重绘闪烁:
' VB.NET 写法匹配你现有代码的风格 SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True) - 重写控件的
OnPaint方法,在固定的左上角位置(就是你之前设的坐标(3,5))直接画复选框:Protected Overrides Sub OnPaint(e As PaintEventArgs) MyBase.OnPaint(e) ' 按系统标准复选框尺寸绘制 ControlPaint.DrawCheckBox(e.Graphics, 3, 5, 13, 13, If(IsSelectAllChecked, ButtonState.Checked, ButtonState.Normal)) End Sub - 这种方式下,复选框的绘制是跟着ListView的重绘周期走的:不管是滚动、窗口拖拽缩放,还是界面失效刷新,每次重绘都会先把整个客户区擦干净,再重新画所有内容,根本不会留残影。
方案2:保留独立CheckBox控件(不用改动原有绘制逻辑)
如果不想动ListView的核心绘制逻辑,只要调整CheckBox的挂载位置,同步监听滚动事件擦除残影就行:
- 别把CheckBox加到
ListView.Controls集合里,改成把CheckBox挂在ListView的父容器上,对齐到ListView的左上角位置就行 - 在自定义ListView类里加消息拦截,捕获垂直滚动、水平滚动、鼠标滚轮滚动这三类消息,等滚动处理完,主动把CheckBox原来位置的区域标记为需要重绘,擦掉残影:
Private Const WM_VSCROLL As Integer = &H115 Private Const WM_HSCROLL As Integer = &H114 Private Const WM_MOUSEWHEEL As Integer = &H20A Protected Overrides Sub WndProc(ByRef m As Message) MyBase.WndProc(m) ' 滚动完成后,触发对应区域重绘 If m.Msg = WM_VSCROLL Or m.Msg = WM_HSCROLL Or m.Msg = WM_MOUSEWHEEL Then Parent?.Invalidate(New Rectangle(Location, SelectAllCheckBox.Size)) End If End Sub
避坑提醒
- 别靠开定时器不停刷新解决残影,纯纯浪费CPU,还容易把界面弄闪
- 别把叠加的控件直接塞到ListView的客户区内部当子控件,这种做法从根上就和ListView的快速块滚动机制不兼容,怎么调都容易出残影
内容的提问来源于stack exchange,提问作者Pavlo Zakharuk
相关产品推荐
相关产品推荐

