VB.NET中从流调整图片大小优化DevExpress Grid图片显示性能
解决方案:DevExpress Grid行切换时图片加载卡顿优化
问题背景
使用DevExpress.XtraGrid的FocusedRowChanged事件时,因存在数千张图片数据,切换行时直接加载原图到PictureEdit导致界面卡顿。需求是:默认显示缩略图,双击图片时再展示原图。
现有代码
If Not String.IsNullOrWhiteSpace(filePath1) Then PictureEdit1.Image = Image.FromStream(New MemoryStream(File.ReadAllBytes(filePath1)), True, False) If Not String.IsNullOrWhiteSpace(filePath2) Then PictureEdit2.Image = Image.FromStream(New MemoryStream(File.ReadAllBytes(filePath2)), True, False) If Not String.IsNullOrWhiteSpace(filePath3) Then PictureEdit3.Image = Image.FromStream(New MemoryStream(File.ReadAllBytes(filePath3)), True, False) If Not String.IsNullOrWhiteSpace(filePath4) Then PictureEdit4.Image = Image.FromStream(New MemoryStream(File.ReadAllBytes(filePath4)), True, False) If Not String.IsNullOrWhiteSpace(filePath5) Then PictureEdit5.Image = Image.FromStream(New MemoryStream(File.ReadAllBytes(filePath5)), True, False) If Not String.IsNullOrWhiteSpace(filePath6) Then PictureEdit6.Image = Image.FromStream(New MemoryStream(File.ReadAllBytes(filePath6)), True, False) End If End If End If End If End If End If Private Sub PictureEditAll_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureEdit1.DoubleClick, PictureEdit2.DoubleClick, PictureEdit3.DoubleClick, PictureEdit4.DoubleClick, PictureEdit5.DoubleClick, PictureEdit6.DoubleClick Dim picEdit As PictureEdit = DirectCast(sender, PictureEdit) Dim f2 As New Preview(picEdit.Image) f2.Show(Me) End Sub
优化方案
1. 实现缩略图生成方法
编写通用方法,根据原图路径生成指定尺寸的缩略图,避免加载完整原图:
Private Function GenerateThumbnail(filePath As String, targetWidth As Integer, targetHeight As Integer) As Image Using originalImage As Image = Image.FromFile(filePath) ' 计算缩放比例,保持图片宽高比 Dim scale As Double = Math.Min(targetWidth / originalImage.Width, targetHeight / originalImage.Height) Dim newWidth As Integer = CInt(originalImage.Width * scale) Dim newHeight As Integer = CInt(originalImage.Height * scale) Dim thumbnail As New Bitmap(newWidth, newHeight) Using g As Graphics = Graphics.FromImage(thumbnail) g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic g.DrawImage(originalImage, 0, 0, newWidth, newHeight) End Using Return thumbnail End Using End Function
2. 异步加载+缓存缩略图
使用异步操作避免阻塞UI线程,同时用字典缓存已生成的缩略图,减少重复计算:
' 缩略图缓存,键为文件路径,值为缩略图 Private thumbnailCache As New Dictionary(Of String, Image) ' 原图路径缓存,用于双击时加载原图 Private picturePaths As New Dictionary(Of PictureEdit, String) Private Async Sub GridView_FocusedRowChanged(sender As Object, e As DevExpress.XtraGrid.Views.Base.FocusedRowChangedEventArgs) Handles GridView1.FocusedRowChanged ' 先清空当前PictureEdit的内容,避免旧图残留 ClearPictureEdits() ' 获取当前行的图片路径(根据你的实际数据结构调整) Dim filePath1 As String = GridView1.GetRowCellValue(e.FocusedRowHandle, "FilePath1").ToString() Dim filePath2 As String = GridView1.GetRowCellValue(e.FocusedRowHandle, "FilePath2").ToString() ' ... 其他路径同理 ' 异步加载每个PictureEdit的缩略图 Await LoadThumbnailAsync(PictureEdit1, filePath1) Await LoadThumbnailAsync(PictureEdit2, filePath2) ' ... 其他PictureEdit同理 End Sub Private Async Function LoadThumbnailAsync(picEdit As PictureEdit, filePath As String) As Task If String.IsNullOrWhiteSpace(filePath) Then picEdit.Image = Nothing picturePaths.Remove(picEdit) Return End If ' 先检查缓存,存在则直接使用 If thumbnailCache.ContainsKey(filePath) Then picEdit.Image = thumbnailCache(filePath) picturePaths(picEdit) = filePath Return End If ' 异步生成缩略图,避免阻塞UI Dim thumbnail As Image = Await Task.Run(Function() GenerateThumbnail(filePath, 200, 200)) ' 回到UI线程赋值 picEdit.Invoke(Sub() picEdit.Image = thumbnail thumbnailCache(filePath) = thumbnail picturePaths(picEdit) = filePath End Sub) End Function Private Sub ClearPictureEdits() PictureEdit1.Image = Nothing PictureEdit2.Image = Nothing ' ... 清空其他PictureEdit picturePaths.Clear() End Sub
3. 双击加载原图优化
双击时直接从文件加载原图,而非依赖PictureEdit中的缩略图,同时注意资源释放:
Private Sub PictureEditAll_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureEdit1.DoubleClick, PictureEdit2.DoubleClick, PictureEdit3.DoubleClick, PictureEdit4.DoubleClick, PictureEdit5.DoubleClick, PictureEdit6.DoubleClick Dim picEdit As PictureEdit = DirectCast(sender, PictureEdit) If picturePaths.ContainsKey(picEdit) Then Dim filePath As String = picturePaths(picEdit) ' 加载原图,使用Using确保资源释放 Using originalImage As Image = Image.FromFile(filePath) ' 克隆原图,避免Preview窗体持有文件锁 Dim clonedImage As Image = originalImage.Clone() Dim f2 As New Preview(clonedImage) f2.Show(Me) End Using End If End Sub
4. 额外优化建议
- 内存管理:当Grid行数量大时,定期清理缓存中不再使用的缩略图(比如监听GridView的RowCount变化,或者使用LRU缓存策略),避免内存溢出。
- 图片格式预处理:如果可能,提前批量生成所有图片的缩略图并保存到本地,切换行时直接加载本地缩略图,进一步提升速度。
- DevExpress控件特性:可以考虑使用DevExpress的
PictureEdit的ZoomMode属性设置为ZoomMode.Fit,避免控件额外的缩放计算;同时开启PictureEdit的AsyncLoad属性(如果版本支持),简化异步加载逻辑。
内容的提问来源于stack exchange,提问作者roy
相关产品推荐
相关产品推荐

