Link to home
Start Free TrialLog in
Avatar of Programgod
Programgod

asked on

How do I dynamically load an image into Crystal Report using vb.net?

I have been searching the web for an answer to my issue.  I have found many articles and forums, which I have used to get where I am, that discuss how to dynamically load images into a Crystal Report using VB.Net.  I have followed the examples to the letter and yet I am unable to get my images onto the report.  I have seen several peoples comments saying that it worked perfectly and that they were able to get it to work.  However, when I run my code and it gets to the point where it tries to read the bytes of the file into the row I get an exception error that states "Unable to cast object of type 'System.Byte[]' to type 'System.IConvertible'.Couldn't store <System.Byte[]> in img Column.  Expected type is Byte."  I am not sure what I am doing wrong, but it does not seem to want to load.  Any assistance would be GREATLY appreciated.
Public Function ImgTable(ByVal ImageFile As String) As DataTable
        ImgTable = New DataTable
        Dim row As DataRow
 
        ImgTable.TableName = "Images"
        ImgTable.Columns.Add("img", GetType(System.Byte))
 
        FileCopy(ImageFile, "C:\test.tif")     '<-- I am only copying the image locally because of a different error
        Dim fs As New IO.FileStream("C:\test.tif", IO.FileMode.Open)
        Dim br As New IO.BinaryReader(fs)
 
        row = ImgTable.NewRow
        row(0) = br.ReadBytes(br.BaseStream.Length)   '<-- This is where I get the error
        ImgTable.Rows.Add(row)
 
        br = Nothing
        fs.Close()
        fs = Nothing
    End Function

Open in new window

Avatar of Mike McCracken
Mike McCracken

I can't swear to it but I don't think Crystal supports TIF files loaded that way.

I believe one of these 2 examples will help
http://www.emoreau.com/Entries/Articles/2006/09/Feeding-Crystal-Reports-from-your-application.aspx

http://www.emoreau.com/Entries/Articles/2006/10/Crystal-Reports--Part-II.aspx

mlmcc
damn, mlmcc was (a lot) quicker then me for posting my link!
Avatar of Programgod

ASKER

Thanks for the quick responses.  Unfortunately, as far as the TIF, I had the same thought and I did try using JPG and BMP and both end up with the same error.  Also, the links provided are some that I have already looked at, but do not provide a solution.  I know I must be doing something wrong because not only do the people that write the article say it works, but the comments down below have people saying that they have also been able to get it to work.  I copied the code exactly, even to the point of creating a specific function to read the data into the datatable.  I just can't seem to figure out why I am getting a cast error when everything has been casted as System.Byte.
Here is a screen print of the actual error that I am seeing.
CRDynamicImageError.JPG
have you tried my example?
And if you have correctly read the comments at the bottom of the article, you would have found that your column as to be defined as an array of bytes (your current definition is for a single byte).

change:
imgTable.Columns.Add("img", GetType(system.Byte))

to:
imgTable.Columns.Add("img", GetType(Byte()))
Upsetting as it is, after I posted my response I did go back and try adding the () after Byte and it worked as far as not erroring out anymore.  However, the image does not show up on the report.  I tried with a JPG, BMP and a TIF but none were showing up.  Any thoughts?
I have been playing with the code a little bit and I still can't seem to get the pictures to show up.  I have attached my code and two screen shots.  The first screen shot is of my dataset as it stands and the other is of the report itself.  Please let me know if you see anything out of the ordinary.
    Public Function BuildReport() As rptCustConcern
        BuildReport = New rptCustConcern
 
        ImgTable(frmMain.dgPict.Rows(0).Cells(0).Tag)
        BuildReport.SetDataSource(BuildDataset)
    End Function
 
    Public Sub ImgTable(ByVal ImageFile As String)
        Dim ImgTable = New dstCustConcern.ImagesDataTable
        Dim row As dstCustConcern.ImagesRow
 
        'FileCopy(ImageFile, "C:\test.tif")
        Dim fs As New IO.FileStream("C:\test.bmp", IO.FileMode.Open, IO.FileAccess.Read)
        Dim br As New IO.BinaryReader(fs)
 
        row = ImgTable.NewRow
        row(0) = br.ReadBytes(br.BaseStream.Length)
        ImgTable.Rows.Add(row)
 
        br = Nothing
        fs.Close()
        fs = Nothing
    End Sub

Open in new window

CRDataset.JPG
CRReport.JPG
I am not sure if this is an issue or not, but I am feeding the report through a dataset.  On the dataset I have a datatable called "Images" that has one column called "img" with a datatype of System.Byte.  I updated the data type to be System.Byte() on the dataset, but when I do that it makes it so that it can't be seen on the report.  However, if I set it at System.Byte I run into the same issue as before because it is expecting a single byte and not an array.  I am chosing the correct datatype in the dataset correct? (System.Byte).  Also, in the examples it would seem that the data is being pushed directly to the report and not through a dataset.  Could my dataset be the issue?
ASKER CERTIFIED SOLUTION
Avatar of Éric Moreau
Éric Moreau
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I did try your example and it worked.  I am attempting to recreate it on my form, while modifying the code a slight bit because I do not have, nor do I want to have, a picturebox on the main form (yes, I know I could hide it but still).  So instead I create the picturebox in the routine that is to save the image into a memorystream and dump the stream into a byte array and then onto the dataset.  However, now I am running into another issue.  I am attempting to find an answer, but figured I would put it up here just in case you had any insight.
CR-GDI-Error.JPG
Ok, I did finally get it to work.  Apparently it does not like to use dynamically created picturebox's, nor does it like to use a picturebox with an image loaded at runtime.  This is kind of discouraging because I am basically creating a log report that may have 1 or more images attached to the log entry.  I want to be able to dynamically add these to the report and not have to have a static picturebox with a preset picture already defined (since this would defeat the purpose of what I am trying to do).  So now I am left with trying to find a way to be able to run through my list of attached images and have them put on the report.  Any ideas on how to overcome this issue?
Ok, after much finagling I was able to get it to work properly.  Turns out that I can dynamically load the images, but the way that I was loading it from the file caused the issue.  Once I get everything working I will post my code so that others can see how I was able to achieve my goal.  However, this is one more thing I am having an issue with.  Since Cyrstal Reports does not seem to believe in AutoSizing things, I am left with trying to figure out a way to rotate/resize the image based on aspect ratio and overall size.  For pictures that are wider than taller I want to be able to rotate, but when I do I get a null value error when it reaches the save to memory point.  I have, indirectly been able to figure out a way to resize the "img" field, but the way I figured out resizes ALL pictures that get added.  I need to be able to resize those on a per picture basis.  If you have any thoughts on these last two issues I would welcome anything ideas you can offer.  If not, I really appreciate all the help you have given me so far.
Ok, I have got everything working as I needed.  Thanks for all your help for the first half of this emoreau.  Your example did help me a lot.  I have attached my code just in case anyone else has the same issue that I had trying to get this to work.  :-)

For all of you attempting this, please remember to put () after System.Byte in the dataset!
    Public dst As DataSet
    Public pbox As New PictureBox
 
    Public Function BuildReport() As rptCustConcern
        BuildReport = New rptCustConcern
        BuildReport.SetDataSource(BuildDataset)
        If CountPics() = 0 Then BuildReport.Section3.SectionFormat.EnableSuppress = True
    End Function
 
    Public Function BuildDataset() As DataSet
        Dim da As New OleDb.OleDbDataAdapter
 
        BuildDataset = New DataSet("dstCustConcern")
 
        RS.Open("select * from cccustomerinfo where custnum = '" & frmMain.txtCustNum.Text & "'", DB, 3, 1)
        da.Fill(BuildDataset, RS, "custinfo")
        If RS.State <> 0 Then RS.Close()
 
        RS.Open("select * from ccconcerndtl where lognum = " & frmMain.txtLogNum.Text, DB, 3, 1)
        da.Fill(BuildDataset, RS, "concerndtl")
        If RS.State <> 0 Then RS.Close()
 
        Dim arrbytes() As Byte
        Dim ms As IO.MemoryStream
 
        Using dt As New DataTable("Images")
            dt.Columns.Add("img", GetType(Byte()))
 
            For i As Integer = 0 To frmMain.dgPict.Rows.Count - 1
                With frmMain.dgPict.Rows(i).Cells(0)
                    Dim fExt As String = .Tag.ToString.Substring(InStrRev(.Tag, "."), Len(.Tag) - InStrRev(.Tag, "."))
 
                    If Not fExt = "xls" And _
                       Not fExt = "xlsx" And _
                       Not fExt = "doc" And _
                       Not fExt = "docx" And _
                       Not fExt = "pdf" Then
                        pbox.Image = Image.FromFile(.Tag)
                        If fExt <> "bmp" Then
                            Dim nBitmap As New Bitmap(pbox.Image)
                            nBitmap.Save(Replace(.Tag, fExt, "bmp"))
 
                            pbox.Image = Image.FromFile(Replace(.Tag, fExt, "bmp"))
                        End If
                        If pbox.Image.PhysicalDimension.Width > pbox.Image.PhysicalDimension.Height Then
                            pbox.Image.RotateFlip(RotateFlipType.Rotate90FlipNone)
                        End If
 
                        ms = New IO.MemoryStream
                        pbox.Image.Save(ms, Imaging.ImageFormat.Bmp)
                        arrbytes = ms.GetBuffer
                        ms.Close()
                        dt.LoadDataRow(New Object() {arrbytes}, True)
                    End If
                End With
            Next
 
            BuildDataset.Tables.Add(dt)
        End Using
    End Function

Open in new window

CRDataset.JPG
thanks for sharing your code.

If I read your code correctly, you are still using a plain old recordset from ADO (your RS variable). Any reason why you are doing this instead of using 100% ADO.Net?