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

自绘大图标ListView:滚动时避免频繁重绘问题求助

解决ListView自绘选中项滚动卡顿问题

问题场景

使用ListView的LargeIcon视图展示数据库图片缩略图,为了避免默认选中时的蓝色图片遮罩,采用自绘实现了文本区蓝色高亮、图片保持原样的选中样式,但多选后滚动时频繁重绘导致控件卡顿,默认绘制无此问题。

原代码

private void listView6_DrawItem(object sender, DrawListViewItemEventArgs e)
{
   if (e.Item.Selected)
    {
        using (Graphics g = e.Graphics)
        {
            //get image
            Image img = thumbList256.Images[e.Item.ImageIndex];
           
            //draw image                    
            e.Graphics.DrawImage(img, new Point(e.Bounds.X + ((e.Bounds.Width - img.Width) / 2), e.Bounds.Y + 3));

            //fill rectangle arount text
            Rectangle textRect = new Rectangle(e.Bounds.Left, e.Bounds.Top + img.Height + 3, e.Bounds.Width, 40);
            e.Graphics.FillRectangle(highlightBrush, textRect);

            //draw text
            e.Graphics.DrawString(e.Item.Text, smallFont, highlightTextBrush, new RectangleF(new PointF(e.Bounds.Left, e.Bounds.Top + img.Height + 3), new SizeF(e.Bounds.Width, 40)), new StringFormat { Alignment = StringAlignment.Center });
        }                
    }
    else
    {                
        e.DrawDefault = true;
    }                        
}

解决建议

  1. 移除错误的Graphics包裹
    代码里用using (Graphics g = e.Graphics)是完全错误的——e.Graphics是ListView控件提供的绘图上下文,不能用using释放,这会导致绘图资源提前回收,引发频繁重绘和异常,直接删除这层using即可。

  2. 复用GDI+资源对象
    每次DrawItem都创建StringFormat、频繁访问字体/画刷会产生大量临时对象,触发GC导致卡顿。把这些对象提前初始化并复用:

    // 类级别的成员变量,初始化一次
    private Brush _highlightBrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
    private Brush _highlightTextBrush = new SolidBrush(Color.White);
    private StringFormat _centerStringFormat = new StringFormat { Alignment = StringAlignment.Center };
    private Font _smallFont;
    
    // 在窗体构造函数或ListView初始化时赋值
    public YourForm()
    {
        InitializeComponent();
        _smallFont = new Font(listView6.Font.FontFamily, 8.5f);
    }
    
    // 记得在窗体关闭时释放资源
    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        _highlightBrush.Dispose();
        _highlightTextBrush.Dispose();
        _centerStringFormat.Dispose();
        _smallFont.Dispose();
        base.OnFormClosed(e);
    }
    
  3. 开启ListView双缓冲
    默认ListView双缓冲可能未开启,手动开启可以减少闪烁和卡顿:

    // 在窗体构造函数里设置
    listView6.DoubleBuffered = true;
    // 如果上面的属性无法直接访问,用反射设置
    typeof(ListView).InvokeMember("DoubleBuffered", BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, listView6, new object[] { true });
    
  4. 优化图片绘制逻辑
    避免每次绘制都从ImageList中取图片,可提前把图片缓存到项的Tag属性里(如果图片不会动态变化):

    // 加载项时把图片存到Tag
    ListViewItem item = new ListViewItem("图片名称");
    item.ImageIndex = index;
    item.Tag = thumbList256.Images[index];
    

    然后在DrawItem里直接取:

    Image img = e.Item.Tag as Image;
    
  5. 统一绘制逻辑,避免混合默认绘制
    混合使用自绘和默认绘制(e.DrawDefault = true)可能导致控件绘制逻辑混乱,建议所有项都用自绘:

    private void listView6_DrawItem(object sender, DrawListViewItemEventArgs e)
    {
        Image img = e.Item.Tag as Image;
        // 绘制图片
        e.Graphics.DrawImage(img, new Point(e.Bounds.X + ((e.Bounds.Width - img.Width) / 2), e.Bounds.Y + 3));
    
        // 绘制文本背景
        Rectangle textRect = new Rectangle(e.Bounds.Left, e.Bounds.Top + img.Height + 3, e.Bounds.Width, 40);
        if (e.Item.Selected)
        {
            e.Graphics.FillRectangle(_highlightBrush, textRect);
            e.Graphics.DrawString(e.Item.Text, _smallFont, _highlightTextBrush, textRect, _centerStringFormat);
        }
        else
        {
            // 用默认背景和文本颜色绘制
            e.Graphics.FillRectangle(SystemBrushes.Window, textRect);
            e.Graphics.DrawString(e.Item.Text, _smallFont, SystemBrushes.ControlText, textRect, _centerStringFormat);
        }
    }
    

内容的提问来源于stack exchange,提问作者Wayne L Waag

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 12:00:27