You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Timer刷新时ListView控件闪烁的问题?

解决VB ListView刷新时的闪烁问题

嘿,我太懂你这种烦恼了——ListView每次刷新就闪一下,看着真闹心!咱们先揪出你现有代码里的几个小问题,再一步步把闪烁的问题解决掉:

先说说现有代码里的“隐形坑”

  1. 重复创建列完全没必要:你的Timer1_Tick里每次都ListView1.Columns.Clear(),然后在GetData里重新加列,这会触发大量不必要的控件重绘,直接加剧闪烁。列只需要初始化一次就行!
  2. 重复设置控件属性:GetData里每次都设置HeaderStyle、View、GridLines这些属性,这些配置在窗体加载时做一次就够了,重复操作只会增加重绘次数。
  3. 多余的Refresh调用:EndUpdate()已经会自动触发控件重绘,最后加的ListView1.Refresh()纯属画蛇添足,反而会多一次无意义的重绘。
  4. Timer的冗余操作:Timer1.Enabled = True在Tick事件里完全没必要,因为Timer本来就是处于启用状态的。

具体解决方案

方案1:优化刷新逻辑,减少不必要的重绘

把列的初始化、控件基础配置移到Form1_Load里,GetData只负责更新数据项,不要动列结构。同时用BeginUpdate()和EndUpdate()包裹更新操作,避免每添加一个项就重绘一次。

方案2:启用ListView的双缓冲

WinForms的ListView默认没开启双缓冲,这是导致闪烁的核心原因之一。我们可以用反射快速开启它:

' 在窗体Load事件里添加这段代码
Dim ctrlType As Type = ListView1.GetType()
Dim pi As PropertyInfo = ctrlType.GetProperty("DoubleBuffered", BindingFlags.Instance Or BindingFlags.NonPublic)
pi.SetValue(ListView1, True, Nothing)
' 额外补充重绘样式,进一步优化
ListView1.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True)
ListView1.UpdateStyles()

方案3:正确使用LockWindowUpdate(可选)

如果还是有轻微闪烁,可以用LockWindowUpdate暂时锁定控件重绘,更新完成后必须解锁(不然控件会卡住):

LockWindowUpdate(ListView1.Handle)
Try
    ' 这里放更新ListView的代码
Finally
    LockWindowUpdate(IntPtr.Zero)
End Try

修改后的完整代码示例

Imports System.Data.SqlClient
Imports System.Reflection ' 用于反射设置双缓冲

Public Class Form1
    Dim conn As SqlConnection
    Dim cmd As SqlCommand
    Dim da As SqlDataAdapter
    Dim ds As DataSet
    Dim itemcoll(100) As String
    Private WithEvents tmr As New Timer With {.Interval = 1000} ' 直接设置初始间隔

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' 初始化ListView基础配置,只做一次
        ListView1.HeaderStyle = ColumnHeaderStyle.None
        ListView1.View = View.Details
        ListView1.GridLines = False
        ' 启用双缓冲
        EnableDoubleBuffering(ListView1)
        ' 初始化列结构
        InitializeListViewColumns()
        ' 启动Timer并加载初始数据
        tmr.Start()
        GetData()
    End Sub

    ' 单独初始化列的方法
    Private Sub InitializeListViewColumns()
        ListView1.Columns.Clear()
        ' 通过读取表结构来创建列(如果表结构固定,也可以直接硬编码列名)
        Using conn As New SqlConnection("Data Source=xxx;Initial Catalog=orders; User ID=xxx;Password=xxx")
            Dim strQ As String = "SELECT productcode, productname, qty from dbo.ordertemptable"
            cmd = New SqlCommand(strQ, conn)
            da = New SqlDataAdapter(cmd)
            ds = New DataSet
            da.FillSchema(ds, SchemaType.Source, "dbo.ordertemptable")
            For i = 0 To ds.Tables(0).Columns.Count - 1
                ListView1.Columns.Add(ds.Tables(0).Columns(i).ColumnName.ToString())
            Next
        End Using
        ' 一次性调整列宽
        ListView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
        ListView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize)
    End Sub

    Private Sub GetData()
        ListView1.BeginUpdate()
        Try
            ListView1.Items.Clear() ' 只清空数据项,不清列
            Using conn As New SqlConnection("Data Source=xxx;Initial Catalog=orders; User ID=xxx;Password=xxx")
                Dim strQ As String = "SELECT productcode, productname, qty from dbo.ordertemptable"
                cmd = New SqlCommand(strQ, conn)
                da = New SqlDataAdapter(cmd)
                ds = New DataSet
                da.Fill(ds, "dbo.ordertemptable")

                For i = 0 To ds.Tables(0).Rows.Count - 1
                    For j = 0 To ds.Tables(0).Columns.Count - 1
                        itemcoll(j) = ds.Tables(0).Rows(i)(j).ToString()
                    Next
                    Dim lvi As New ListViewItem(itemcoll)
                    ListView1.Items.Add(lvi)
                Next
                ' 最后统一调整列宽,避免循环内重复操作
                ListView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
            End Using
        Finally
            ListView1.EndUpdate() ' 确保即使出错也会结束更新,避免控件卡死
        End Try
    End Sub

    ' 启用双缓冲的工具方法
    Private Sub EnableDoubleBuffering(ctrl As Control)
        Dim ctrlType As Type = ctrl.GetType()
        Dim pi As PropertyInfo = ctrlType.GetProperty("DoubleBuffered", BindingFlags.Instance Or BindingFlags.NonPublic)
        pi.SetValue(ctrl, True, Nothing)
        ctrl.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint, True)
        ctrl.UpdateStyles()
    End Sub

    Private Sub tmr_Tick(sender As Object, e As EventArgs) Handles tmr.Tick
        GetData() ' 直接调用更新数据即可
    End Sub
End Class

额外小建议

如果你的数据量较大,建议做增量更新:只添加新录入的数据、删除已不存在的数据,而不是每次清空所有项重新加载,这样能进一步减少重绘,提升性能。

内容的提问来源于stack exchange,提问作者anthony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:31:50