First off - thanks for all the information and snippets around here. Appreciate it. My problem;
I'm trying to save some datagridview data, including images, to an XML file. Then trying to read it back in the grid again. I'm using a dataset & table (unbounded) for easy XML writing as as far as I'm aware - binding doesnt work with an imagecolumn.
I can save the data, then read it in again. However - when I try to save the data again - it fails on the following line in "Sub Savetofile":
Img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
with the following error:
An unhandled exception of type System.Runtime.InteropServices.ExternalException' occurred in System.Drawing.dll Additional information: A generic error occurred in GDI+.
Any ideas what I'm missing?
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
That error is a catch-all which includes anything from file access denied to running out of resources. In this case it is because you are reusing the same MemoryStream
over and over.
But you do not have to manually convert the image and "compress" them. If the DataTable
has a Byte()
column, the DataGridView
will know how to convert that for use as an Image. The WriteXML
method will also automatically encode the bytes as a Base64 string (and read it back).
Sample:
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)
XmlWriteMode.WriteSchema
is very important. When you read back the data, you want the destination DataTable
to know to convert that Base64 string back to a Byte
array rather than displaying iVBORw0KGgoAAAANSUhEUgAAABA...
Using
block. This will dispose of the MemoryStream
at the end and allow it to free any resources it allocates. This is missing in your code and as a result the MemoryStream is accumulating data in addition to leaking:
334, 684, 1014, 1361...
. It is accumulating image data and all but the first image will be corrupt resulting in the GDI error.Code to test for the round trip:
Dim dtXYZ = New DataTable()
' or use a dataset
dtXYZ.ReadXml("C:\Temp\DTX.xml")
dgv1.DataSource = dtXYZ
The same data for both before and after.