sha*_*rgy 9 vb.net locking datagridview image save
我正在使用以下代码将JPG放入一个DataGridView
图像单元格中.
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = Image.FromFile(strFileName)
DataGridView4.Rows.Add()
DataGridView4.Rows(DataGridView4.Rows().Count - 1).Cells(0).Value = inImg
End If
Run Code Online (Sandbox Code Playgroud)
问题是我需要从程序中保存这个文件,但我收到的消息是该文件正在被另一个程序使用.
所以我试图inImg.Dispose()
在结束之前添加if,但之后程序不再显示图像了DataGridView
.
如何在DataGridView
不锁定图像的情况下添加图像?
谢谢
Chr*_*ris 20
当您使用该Image.FromFile(strFileName)
方法创建时Image
,该方法将锁定该文件,直到您释放该文件Image
.确切原因解释如下.这就是为什么你不能用这种方法多次访问同一个图像文件.
你可以改为:
FileStream
或MemoryStream
您从图像文件创建的.以下是可能实现SafeImageFromFile
不锁定图像文件的自定义方法:
Public Shared Function SafeImageFromFile(path As String) As Image
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End using
End Function
Run Code Online (Sandbox Code Playgroud)
要么
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Using ms As New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Using
End Function
Run Code Online (Sandbox Code Playgroud)
用法
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = SafeImageFromFile(strFileName)
Dim index as integer = DataGridView4.Rows.Add()
DataGridView4.Rows(index).Cells(0).Value = inImg
End If
Run Code Online (Sandbox Code Playgroud)
重要的提示
在这里,我创建FileStream
或MemoryStream
使用一个Using
语句来确保流被释放.它在我的系统上工作正常,它似乎也适用于你,虽然 MSDN说有关Image.FromStream(stream)方法:
您必须在图像的生命周期内保持流打开.
这句话的原因在这里解释:KB814675 Bitmap和Image构造函数依赖项
GDI +以及System.Drawing命名空间可以推迟原始图像位的解码,直到图像需要这些位. 另外,即使在图像被解码之后,GDI +也可以确定丢弃用于大位图的存储器并且稍后重新解码更有效.因此,GDI +必须能够在Bitmap或Image对象的生命周期内访问图像的源位.
为了保持对源位的访问,GDI +锁定任何源文件,并强制应用程序在Bitmap或Image对象的生命周期内维持任何源流的生命周期.
因此,知道上面的代码可能会GDIexceptions
因为使用释放流而生成Using
.从文件或图像创建过程中保存图像时可能会发生这种情况.从这个线程加载来自流的图像而不保持流打开和Hans Passant的评论他们修复了Vista版本的gdiplus.dll中的索引像素格式的几个问题.,它只会在XP上发生.
为避免这种情况,您需要保持流打开.方法是:
Public Shared Function SafeImageFromFile(path As String) As Image
Dim fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End Function
Run Code Online (Sandbox Code Playgroud)
要么
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Dim ms = New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Function
Run Code Online (Sandbox Code Playgroud)
但是那些最后的方法有一些缺点,比如不释放流(内存问题),并且在丢失范围之前它们违反规则CA2000 Dispose对象.
知识库文章给出了一些解决方法:
创建非索引图像
这种方法要求新图像采用非索引像素格式(每像素超过8位),即使原始图像采用索引格式.此解决方法使用Graphics.DrawImage()方法将图像复制到新的Bitmap对象:
- 从流,内存或文件构造原始位图.
- 创建一个相同大小的新位图,像素格式超过每像素8位(BPP).
- 使用Graphics.FromImage()方法获取第二个Bitmap的Graphics对象.
- 使用Graphics.DrawImage()将第一个Bitmap绘制到第二个Bitmap上.
- 使用Graphics.Dispose()来处理Graphics.
- 使用Bitmap.Dispose()来处理第一个Bitmap.
创建索引图像
此解决方法以索引格式创建Bitmap对象:
- 从流,内存或文件构造原始位图.
- 创建一个与第一个Bitmap具有相同大小和像素格式的新Bitmap.
- 使用Bitmap.LockBits()方法以其原生像素格式锁定两个Bitmap对象的整个图像.
- 使用Marshal.Copy函数或其他内存复制功能将图像位从第一个位图复制到第二个位图.
- 使用Bitmap.UnlockBits()方法解锁两个Bitmap对象.使用Bitmap.Dispose()来处理第一个Bitmap.
这是一个非索引图像创建的实现,基于知识库文章和这个答案/sf/answers/558107441/ 你最好的选择是创建一个像素完美的图像副本 - 虽然YMMV(与某些类型的图像可能有多个帧,或者您也可能需要复制调色板数据.)但对于大多数图像,这有效:
Private Shared Function SafeImageFromFile(path As String) As Bitmap
Dim img As Bitmap = Nothing
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
End Using
Return img
End Function
Run Code Online (Sandbox Code Playgroud)
有人表示重要的是以FileStream
读模式打开(FileAccess.Read
).
是的,但是如果你不使用Using
语句就会更加敏感,所以你不释放流,或者在多线程上下文中:FileAccess.Write
不合适,并且FileAccess.ReadWrite
不是必需的,但是用FileAccess.Read
模式打开流不会阻止一个IO.Exception
如果另一个程序(或在多线程上下文你的)已经打开与另一模式比文件FileAccess.Read
.
如果您希望能够显示图像并同时能够将数据保存到文件中,由于您没有使用这些方法锁定文件,您应该能够保存图像(删除/覆盖以前的图像)文件)使用该Image.Save
方法.