自绘大图标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; } }
解决建议
移除错误的
Graphics包裹
代码里用using (Graphics g = e.Graphics)是完全错误的——e.Graphics是ListView控件提供的绘图上下文,不能用using释放,这会导致绘图资源提前回收,引发频繁重绘和异常,直接删除这层using即可。复用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); }开启ListView双缓冲
默认ListView双缓冲可能未开启,手动开启可以减少闪烁和卡顿:// 在窗体构造函数里设置 listView6.DoubleBuffered = true; // 如果上面的属性无法直接访问,用反射设置 typeof(ListView).InvokeMember("DoubleBuffered", BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, listView6, new object[] { true });优化图片绘制逻辑
避免每次绘制都从ImageList中取图片,可提前把图片缓存到项的Tag属性里(如果图片不会动态变化):// 加载项时把图片存到Tag ListViewItem item = new ListViewItem("图片名称"); item.ImageIndex = index; item.Tag = thumbList256.Images[index];然后在DrawItem里直接取:
Image img = e.Item.Tag as Image;统一绘制逻辑,避免混合默认绘制
混合使用自绘和默认绘制(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
相关产品推荐
相关产品推荐

