GDI+ 将带有图像的 Datagridview 数据保存到 XML 时出现一般错误

Generic error in GDI+ saving Datagridview data with images to XML

首先 - 感谢您提供的所有信息和片段。欣赏它。我的问题;

我正在尝试将一些 datagridview 数据(包括图像)保存到 XML 文件中。然后尝试再次在网格中读回它。据我所知,我正在使用数据集 & table(无界)来轻松 [​​=26=] 写作 - 绑定不适用于图像列。

我可以保存数据,然后再读入。但是 - 当我再次尝试保存数据时 - 它在 "Sub Savetofile" 中的以下行失败:

Img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg) 

出现以下错误:

An unhandled exception of type System.Runtime.InteropServices.ExternalException' occurred in System.Drawing.dll Additional information: A generic error occurred in GDI+.

知道我遗漏了什么吗?

 Private Sub SaveToFile(sender As Object, e As EventArgs) Handles SaveToolStripMenuItem.Click
    Dim rows As Integer = DataGridView1.Rows.Count - 1
    Dim cols As Integer = DataGridView1.Columns.Count - 1
    Dim MyByte As Byte() = Nothing
    Dim Img As Image = Nothing
    Dim ms = New MemoryStream()
    DataSet1.Observations.Rows.Clear()

    For i = 0 To rows
        For j = 0 To cols
            If j = 0 Then
                Img = DataGridView1.Rows(i).Cells(j).Value
                Img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
                MyByte = ms.ToArray()
                DataSet1.Observations.Rows.Add.Item(1) = Compress(MyByte)
                ms.close()
            ElseIf j >= 1 Then
                If DataGridView1.Rows(i).Cells(j).Value IsNot Nothing Then
                    DataSet1.Observations.Rows(i).Item(j + 1) = DataGridView1.Rows(i).Cells(j).Value.ToString
                End If
            End If
        Next
    Next
    File.Delete("C:\test2.quad")
    DataSet1.WriteXml("C:\test2.quad")
End Sub
Private Sub OpenFile(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click

    Dim ms = New MemoryStream()
    Dim MyByte As Byte()

    DataSet1.Clear()
    DataGridView1.Rows.Clear()
    DataSet1.ReadXml("C:\test2.quad")
    Dim rows As Integer = DataSet1.Observations.Rows.Count - 2
    Dim cols As Integer = DataSet1.Observations.Columns.Count - 1

    For i = 0 To rows
        For j = 1 To cols
            If j = 1 Then
                MyByte = Decompress(DataSet1.Observations.Rows(i).Item(1))
                Dim stream As New MemoryStream(MyByte)
                DataGridView1.Rows.Add(Image.FromStream(stream))
                stream.Close()
            ElseIf j >= 2 And DataSet1.Observations.Rows(i).Item(j) IsNot Nothing Then
                DataGridView1.Rows(i).Cells(j - 1).Value = DataSet1.Observations.Rows(i).Item(j).ToString
            End If
        Next
    Next


End Sub

该错误是一个包罗万象的错误,包括从拒绝文件访问到 运行 资源不足的所有内容。在这种情况下,这是因为您一遍又一遍地重复使用相同的 MemoryStream

但您不必手动转换图像和 "compress" 它们。如果 DataTable 有一个 Byte() 列,DataGridView 将知道如何转换它以用作图像。 WriteXML 方法也会自动将字节编码为 Base64 字符串(并读回)。

样本:

Dim dtX = New DataTable("dtX")

Dim imgs As Image() = {My.Resources.ballblack, My.Resources.ballblue,
                 My.Resources.ballgreen, My.Resources.ballorange,
                 My.Resources.ballpurple, My.Resources.ballred,
                 My.Resources.ballyellow}

' columns to use
dtX.Columns.Add(New DataColumn("Name", GetType(String)))
dtX.Columns.Add(New DataColumn("Descr", GetType(String)))
dtX.Columns.Add(New DataColumn("Img", GetType(Byte())))

Dim dr As DataRow
Dim g As Int32
For n As Int32 = 0 To 9
    dr = dtX.NewRow

    dr(0) = RD.GetNames(2)
    dr(1) = RD.GetLorem(40)
    g = RNG.Next(0, 7)          ' pick random index
    Using ms As New MemoryStream
        imgs(g).Save(ms, ImageFormat.Png)
        dr(2) = ms.ToArray()
    End Using

    dtX.Rows.Add(dr)
Next

' "Before"
dgv1.DataSource = dtX
' save to XML
dtX.WriteXml("C:\Temp\DTX.xml", XmlWriteMode.WriteSchema)
  • RD只是一个随机数据生成器。忽略它。
  • XmlWriteMode.WriteSchema 非常重要。当您读回数据时,您希望目标 DataTable 知道将该 Base64 字符串转换回 Byte 数组而不是显示 iVBORw0KGgoAAAANSUhEUgAAABA...
  • 还要注意 Using 块。这将在最后处理 MemoryStream 并允许它释放它分配的任何资源。这 在您的代码 中缺失,因此 MemoryStream 除了泄漏之外还在累积数据:
    • 如果我在非常小的图像上使用相同的图像,流的大小报告 334, 684, 1014, 1361...。它正在累积图像数据,除第一个图像外的所有图像都会损坏,从而导致 GDI 错误。

往返测试代码:

Dim dtXYZ = New DataTable()

' or use a dataset
dtXYZ.ReadXml("C:\Temp\DTX.xml")
dgv1.DataSource = dtXYZ

前后数据相同。