Read Image from MS Access Database from C# WPF Application

0

My question is about:

  • So I can connect to an MS Access Database and read from it.
  • Store the data from the data table.
  • Cannot convert the byte[] data to a displayable image for my WPF application
  • I am trying to retrieve a jpeg or png image

Here is my code for Database Connection (Successful code):

public static void getTableItems(DataTable dt, int bodySectionNo)
    {
        // gets the oledbconnection object to open and access
        var con = GetConnection();

        try
        {
            con.Open(); // opens db connection
            // creates the sql query for db items change bodysection = # for different bodySections
            OleDbCommand command = new OleDbCommand("SELECT itemNo, itemName, NSN, bodySection, Image.FileName, Image.FileData, Image.FileType  FROM tblItems WHERE bodySection = " + bodySectionNo + ";", con);

            OleDbDataAdapter oleAdapter = new OleDbDataAdapter(command); // executes the command and retrieves the data from the db

            oleAdapter.Fill(dt); // fills the datatable with the query results
        }
        catch(Exception e)
        {
            // writes to console any errors for this connection
            Console.WriteLine("ERROR: getTableItemsForHead, " + e.Message);
        }
        finally
        {
            con.Close(); // closes db connection
        }
    }

In my MainWindow.xaml.cs file I am trying to read through the datatable, store the data, and then pass the data into an element for the mainwindow to display. Here is my MainWindow.xaml.cs code (Unsuccessful code):

private void populateComboBox(DataTable dt, ComboBox cb)
    {
        foreach (DataRow row in dt.Rows)
        {
            WrapPanel wp = new WrapPanel();
            TextBlock txtItemName = new TextBlock();
            TextBlock txtNSN = new TextBlock();
            Image img = new Image();

            // creates a textbox and adds it to wrappanel
            txtItemName.Text = Convert.ToString(row["itemName"]) + " ";
            wp.Children.Add(txtItemName);

            // creates a textbox and adds it to wrappanel
            txtNSN.Text = Convert.ToString(row["NSN"]) + " ";
            wp.Children.Add(txtNSN);

            // stores image properties from datatable
            var fileName = row["Image.FileName"];
            var fileData = (byte[])row["Image.FileData"];
            var fileType = row["Image.FileType"];

            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.CreateOptions = BitmapCreateOptions.None;
            bi.CacheOption = BitmapCacheOption.Default;
            bi.StreamSource = new MemoryStream(fileData);
            bi.EndInit();

            img.Source = bi;

            wp.Children.Add(img);

            cb.Items.Add(wp);
        }
    }

I have tried multiple different ways to convert the byte[] to bitmap, bitmapimage, bitmapsource, image, imagesource, etc.. and I can not find the snippet to resolve this "No Imaging component suitable to complete this operation was found" It breaks on the bi.EndInit(); line. enter image description here

System.NotSupportedException was unhandled by user code
HResult=-2146233067 Message=No imaging component suitable to complete this operation was found. Source=PresentationCore
StackTrace: at System.Windows.Media.Imaging.BitmapDecoder.SetupDecoderFromUriOrStream(Uri uri, Stream stream, BitmapCacheOption cacheOption, Guid& clsId, Boolean& isOriginalWritable, Stream& uriStream, UnmanagedMemoryStream& unmanagedMemoryStream, SafeFileHandle& safeFilehandle) at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache) at System.Windows.Media.Imaging.BitmapImage.FinalizeCreation() at System.Windows.Media.Imaging.BitmapImage.EndInit() at CWEDA_take_1.SecondWindow.populateComboBox(DataTable dt, ComboBox cb) in P:\All Documents\BBMD 18\CWEDA EH\CWEDA Design 1\CWEDA take 1\SecondWindow.xaml.cs:line 286 at CWEDA_take_1.SecondWindow..ctor() in P:\All Documents\BBMD 18\CWEDA EH\CWEDA Design 1\CWEDA take 1\SecondWindow.xaml.cs:line 36
InnerException: ErrorCode=-2003292336 HResult=-2003292336 Message=The component cannot be found. (Exception from HRESULT: 0x88982F50) InnerException:

EDIT: file data that is in the byte[] **EDIT: file data that is in the byte[]**

EDIT: SOLUTION!! I figured this out a bit ago, but forgot to upload my solution:

I had to determine the image type first by searching for the header block.

    // Converts byte array from database to image source
    public static BitmapImage convertByteSource(byte[] oleFieldBytes)
    {

        if (oleFieldBytes == null || oleFieldBytes.Length == 0) return null;


        const string BITMAP_ID_BLOCK = "BM";
        const string JPG_ID_BLOCK = "\u00FF\u00D8\u00FF";
        const string PNG_ID_BLOCK = "\u0089PNG\r\n\u001a\n";
        byte[] imageBytes;

        // Get a UTF7 Encoded string version
        Encoding u8 = Encoding.UTF7;
        string strTemp = u8.GetString(oleFieldBytes);

        // Get the first 300 characters from the string
        string strVTemp = strTemp.Substring(0, 300);

        // Search for the block
        int iPos = -1;
        if (strVTemp.IndexOf(BITMAP_ID_BLOCK) != -1)
            iPos = strVTemp.IndexOf(BITMAP_ID_BLOCK);
        else if (strVTemp.IndexOf(JPG_ID_BLOCK) != -1)
            iPos = strVTemp.IndexOf(JPG_ID_BLOCK);
        else if (strVTemp.IndexOf(PNG_ID_BLOCK) != -1)
            iPos = strVTemp.IndexOf(PNG_ID_BLOCK);
        else
            throw new Exception("Unable to determine header size for the OLE Object");

        // From the position above get the new image
        if (iPos == -1)
            throw new Exception("Unable to determine header size for the OLE Object");

        //Array.Copy(
        imageBytes = new byte[oleFieldBytes.LongLength - iPos];
        MemoryStream ms = new MemoryStream();
        ms.Write(oleFieldBytes, iPos, oleFieldBytes.Length - iPos);
        imageBytes = ms.ToArray();
        ms.Close();

        // creates new bitmapimage
        BitmapImage bi = new BitmapImage();
        bi.BeginInit();// inits bitmapimage
        bi.CreateOptions = BitmapCreateOptions.None;
        bi.CacheOption = BitmapCacheOption.Default;
        bi.StreamSource = new MemoryStream(imageBytes);
        bi.EndInit();

        return bi;
    }
c#
ms-access
wpf-controls
asked on Stack Overflow Apr 17, 2018 by EricHansen • edited Feb 5, 2020 by leonheess

1 Answer

0

The code in this page looks somewhat different from what you have there:
http://csharphelper.com/blog/2015/07/display-images-in-an-access-database-in-wpf-and-c/

In case this works and the link breaks in the future. the code does:

if (reader.IsDBNull(6))
    imgCover.Source = null;
else
    imgCover.Source =
        BytesToImage((byte[])reader.GetValue(6));

That method:

// Convert a byte array into a BitmapImage.
private static BitmapImage BytesToImage(byte[] bytes)
{
    var bm = new BitmapImage();
    using (MemoryStream stream = new MemoryStream(bytes))
    {
        stream.Position = 0;
        bm.BeginInit();
        bm.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
        bm.CacheOption = BitmapCacheOption.OnLoad;
        bm.UriSource = null;
        bm.StreamSource = stream;
        bm.EndInit();
    }
    return bm;
}
answered on Stack Overflow Apr 17, 2018 by Andy

User contributions licensed under CC BY-SA 3.0