I have an application that creates WPF Image controls on-the-fly to display images from a SQL Server database table. The panels that display the controls get replaced frequently. I use MemoryStreams to allow the controls to access the byte arrays loaded from the database. I cannot close these streams as long as the Image controls are displayed and still have the images displayed. My concern is that these streams will be left open until the Garbage Collector gets around to disposing of the Image controls and hopefully closing the streams too. I will probably end up just making sure that I close the streams or dispose of the Image controls before the panels are replaced but would like to know if there is an issue with leaving these streams open and if there is a way around this.
Here's the code that I am using.
public class ImageToSql
{
private static OpenFileDialog _openFileDialog = new OpenFileDialog();
static ImageToSql()
{
_openFileDialog = new OpenFileDialog();
_openFileDialog.AddExtension = true;
_openFileDialog.CheckFileExists = true;
_openFileDialog.DefaultExt = ".jpg";
_openFileDialog.Filter = "JPEG files (*.jpg)|*.jpg|All files (*.*)|*.*";
_openFileDialog.FilterIndex = 1;
_openFileDialog.Multiselect = false;
}
public static BitmapImage LoadImageFromFile(string caption)
{
BitmapImage bi = new BitmapImage();
_openFileDialog.Title = caption;
if (_openFileDialog.ShowDialog() == true)
{
bi.BeginInit();
bi.StreamSource = File.OpenRead(_openFileDialog.FileName);
bi.EndInit();
}
return bi;
}
public static int StoreImage(string connectionString, string sql, string imageParameterName, BitmapImage bitmapImage)
{
SqlConnection conn = new SqlConnection(connectionString);
try
{
conn.Open();
sql += ";SELECT ##IDENTITY";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
byte[] data = new byte[bitmapImage.StreamSource.Length];
bitmapImage.StreamSource.Seek(0, SeekOrigin.Begin);
bitmapImage.StreamSource.Read(data, 0, data.Length);
cmd.Parameters.Add(imageParameterName, System.Data.SqlDbType.VarBinary, -1).Value = data;
object obj = cmd.ExecuteScalar();
return Int32.Parse(obj.ToString());
}
}
finally
{
conn.Close();
}
}
public static BitmapImage RetrieveImage(string connectionString, string sql, string imageIDParameterName, int imageID)
{
BitmapImage bi = new BitmapImage();
SqlConnection conn = new SqlConnection(connectionString);
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add(imageIDParameterName, System.Data.SqlDbType.Int).Value = imageID;
byte[] data = (byte[])cmd.ExecuteScalar();
MemoryStream ms = new MemoryStream(data);
ms.Seek(0, SeekOrigin.Begin);
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
}
}
finally
{
conn.Close();
}
return bi;
}
}
Below is how this code is called. "_fileImage" and "_sqlImage" are Image controls on my test Window. At one point, I was using a stream to read the file from disk in "LoadImageFromFile" and think I was able to close that stream while still displaying the image in the Image control. So, MemoryStream seems to be special somehow.
BitmapImage bi = ImageToSql.LoadImageFromFile("Select Image to Save");
_fileImage.Source = bi;
int imageID = ImageToSql.StoreImage(connString,
"INSERT INTO PV_Image (Description, Image) VALUES ('Some Image', #pImage)",
"#pImage", bi);
_sqlImage.Source = ImageToSql.RetrieveImage(connString,
"SELECT Image FROM PV_Image WHERE ImageID = #pImageID",
"#pImageID", imageID);
You can dispose the memory stream once it is used using the using pattern:
using (Stream stream = new MemoryStream(data))
{
BitmapImage image = new BitmapImage():
stream.Position = 0;
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
image.Freeze();
return image;
}
Related
With this code i am able to get one image from Mysql database in C# in WPF. But i have a table in database that contaon all countries's names and their
flag etc. When i query to select many or all countries folling error occur
"Cannot set the initializing state more than once" at this line "bi.BeginInit()".
Thanks in advance.
string co = null;
string na = null;
string le = null;
string gn = null;
string A = "pak";
BitmapImage bi = new BitmapImage();
try
{
MySqlCommand cmd = new MySqlCommand("Select Code,Name,LifeExpectancy,GNP,flg from country where Name REGEXP '" + A + "'", connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
co = dataReader["Code"].ToString();
na = dataReader["Name"].ToString();
le = dataReader["LifeExpectancy"].ToString();
gn = dataReader["GNP"].ToString();
Byte[] bindata = (Byte[])dataReader["flg"];
MemoryStream strm = new MemoryStream();
strm.Write(bindata, 0, bindata.Length);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
bi.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
dt.Rows.Add(co, na, le, gn, bi);
dataGridCustomers.ItemsSource = dt.DefaultView;
}
}
catch (MySqlException ex)
{
MessageBox.Show(ex.ToString());
}
The reason for this is because you are creating the BitmapImage outside of the loop. So Once you get in the loop it keeps trying to set the initializing state on the same image. As your error states you can't do this. What you need to do is create a new BitmapImage on each iteration. Which can be accomplished by moving the line
BitmapImage bi = new BitmapImage();
To the inside of the loop. Unless there is a specific reason you are initializing it where you are.
string co = null;
string na = null;
string le = null;
string gn = null;
string A = "pak";
try
{
MySqlCommand cmd = new MySqlCommand("Select Code,Name,LifeExpectancy,GNP,flg from country where Name REGEXP '" + A + "'", connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
BitmapImage bi = new BitmapImage();
co = dataReader["Code"].ToString();
na = dataReader["Name"].ToString();
le = dataReader["LifeExpectancy"].ToString();
gn = dataReader["GNP"].ToString();
Byte[] bindata = (Byte[])dataReader["flg"];
MemoryStream strm = new MemoryStream();
strm.Write(bindata, 0, bindata.Length);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
bi.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
dt.Rows.Add(co, na, le, gn, bi);
dataGridCustomers.ItemsSource = dt.DefaultView;
}
}
catch (MySqlException ex)
{
MessageBox.Show(ex.ToString());
}
EDIT: In the code above, the System.Drawing.Image is entirely redundant. You should directly create the BitmapImage from the byte array like this:
var bindata = (byte[])dataReader["flg"];
var bi = new BitmapImage();
using (var stream = new MemoryStream(bindata))
{
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.StreamSource = stream;
bi.EndInit();
}
I put my pictures of letters in a separate table and I defined an foriegn key for that
I now want to click on any data in the data grid view to show the photos of that level in the control of the image.
I wrote the order for the retrieval of the photos, Of course, I do not know if it's right and I do not know how to control the image.
code
SqlConnection imageConnection = new SqlConnection(#"Data Source = ZAHRA; Initial Catalog = Baygani; inte");
SqlCommand imageCommand = new SqlCommand(#"select Pictures.pic1,Pictures.pic2,Pictures.pic3,Pictures.pic4,Pictures.pic5,Pictures.pic6,Pictures.pic7,Pictures.pic8,Pictures.pic9,
Pictures.pic10 from Pictures,Latters where Pictures.idlatter=Latters.id ", imageConnection);
imageConnection.Open();
SqlDataReader imageReader = imageCommand.ExecuteReader();
string imageFilename = (string)imageReader.GetValue(0);
byte[] imageBytes = (byte[])imageReader.GetValue(1);
MemoryStream ms = new MemoryStream(imageBytes);
Assuming that you are using Bindings to display your data in your UI and thus using viewmodels, I would suggest utilizing an ImageSource.
To initialize an ImageSource from a byte array I wrote the following method:
public ImageSource LoadImageSourceFromBytes(byte[] imageBytes)
{
BitmapImage bitmapImage = new BitmapImage();
using (MemoryStream imageStream = new MemoryStream(imageBytes))
{
bitmapImage.BeginInit();
bitmapImage.StreamSource = imageStream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
}
bitmapImage.Freeze();
return bitmapImage;
}
This question already has answers here:
Byte array to image conversion
(14 answers)
Display image from byte[ ]
(1 answer)
Closed 5 years ago.
I'm new to WPF programming and Microsoft SQL server. I want to insert and retrieve an image to/from a database. I learned about converting an image (Windows.Controls.Image) to byte[] and storing it to a database, but I couldn't convert from byte[] to Image back to display it in a WPF window.
private Image byteArrayToImage(byte[] arr)
{
MemoryStream stream = new MemoryStream();
stream.Write(arr, 0, arr.Length);
stream.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(stream); // Exception
BitmapImage returnImage = new BitmapImage();
returnImage.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
returnImage.StreamSource = ms;
returnImage.EndInit();
Image ans = new Image();
ans.Source = returnImage;
return ans;
}
Output:
System.ArgumentException: 'Parameter is not valid.'
private byte[] imageToArray(System.Drawing.Image img) // Work well
{
MemoryStream ms = new MemoryStream();
FileInfo fi = new FileInfo(tempData); // File name
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] pic = ms.ToArray();
return pic;
}
first, create a static class for your PictureHelper. You must import the BitmapImage that is used in WPF, i think its the using System.Drawing.Imaging;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;
using Application = System.Windows.Forms.Application;
using Size = System.Drawing.Size;
public static class PictureHelper
{
public static BitmapImage GetImage(object obj)
{
try
{
if (obj == null || string.IsNullOrEmpty(obj.ToString())) return new BitmapImage();
#region Picture
byte[] data = (byte[])obj;
MemoryStream strm = new MemoryStream();
strm.Write(data, 0, data.Length);
strm.Position = 0;
Image img = Image.FromStream(strm);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
return bi;
#endregion
}
catch
{
return new BitmapImage();
}
}
public static string PathReturner(ref string name)
{
string filepath = "";
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = false;
openFileDialog.Filter = #"Image Files(*.jpeg;*.bmp;*.png;*.jpg)|*.jpeg;*.bmp;*.gif;*.png;*.jpg";
openFileDialog.RestoreDirectory = true;
openFileDialog.Title = #"Please select an image file to upload.";
MiniWindow miniWindow = new MiniWindow();
miniWindow.Show();
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
filepath = openFileDialog.FileName;
name = openFileDialog.SafeFileName;
}
miniWindow.Close();
miniWindow.Dispose();
return filepath;
}
public static string Encryptor(this string safeName)
{
string extension = Path.GetExtension(safeName);
string newFileName = String.Format(#"{0}{1}{2}", Guid.NewGuid(), DateTime.Now.ToString("MMddyyyy(HHmmssfff)"), extension);
newFileName = newFileName.Replace("(", "").Replace(")", "");
return newFileName;
}
public static Bitmap ByteToBitmap(this byte[] blob)
{
MemoryStream mStream = new MemoryStream();
byte[] pData = blob;
mStream.Write(pData, 0, Convert.ToInt32(pData.Length));
Bitmap bm = new Bitmap(mStream, false);
mStream.Dispose();
return bm;
}
public static byte[] BitmapToByte(this Image img)
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, ImageFormat.Png);
stream.Close();
byteArray = stream.ToArray();
}
return byteArray;
}
}
this is for retrieving the class (in my case, Candidate) with picture
public SortableBindingList<Candidate> RetrieveManyWithPicture(Candidate entity)
{
var command = new SqlCommand { CommandText = "RetrievePictureCandidates", CommandType = CommandType.StoredProcedure };
command.Parameters.AddWithValue("#CandidateId", entity.CandidateId).Direction = ParameterDirection.Input;
DataTable dt = SqlHelper.GetData(command); //this is where I retrieve the row from db, you have your own code for retrieving, so make sure it works.
var items = new SortableBindingList<Candidate>();
if (dt.Rows.Count <= 0) return items;
foreach (DataRow row in dt.Rows)
{
Candidate item = new Candidate();
item.CandidateId = row["CandidateId"].GetInt();
item.LastName = row["LastName"].GetString();
item.FirstName = row["FirstName"].GetString();
item.PictureId = row["PictureId"].GetInt();
item.PhotoType = PictureHelper.GetImage(row["Photo"]); //in my db, this is varbinary. in c#, this is byte[]
items.Add(item);
}
return items;
}
this is for uploading image from wpf to db which I used on my button
private void UploadButton_Click(object sender, EventArgs e)
{
string safeName = "";
string pathName = PictureHelper.PathReturner(ref safeName);
PictureViewModel vm = new PictureViewModel();
if (pathName != "")
{
safeName = safeName.Encryptor();
FileStream fs = new FileStream(pathName, FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data, 0, Convert.ToInt32(fs.Length));
fs.Close();
PicNameLabel.Text = safeName;
vm.Entity.Name = safeName; //this is the byte[]
Bitmap toBeConverted = PictureHelper.ByteToBitmap(data); //convert the picture before sending to the db
vm.Entity.Photo = PictureHelper.BitmapToByte(toBeConverted);
vm.Entity.Path = pathName;
CandidatePictureBox.Image = toBeConverted;
vm.Insert(vm.Entity);
}
}
this is the method to save the picture
public bool Insert(Picture entity)
{
var command = new SqlCommand();
try
{
command.CommandText = "AddPicture";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#Name", entity.Name).Direction = ParameterDirection.Input;
command.Parameters.AddWithValue("#Photo", entity.Photo).Direction = ParameterDirection.Input;
int result = SqlHelper.ExecuteNonQuery(command); //executenonquery will save the params to the db
return true;
}
catch (Exception)
{
return false;
}
}
I am getting the byte in database but I cannot convert it to bitmap, I got an exception in Parameter. This is what I've done so far.
con.ConnectionString = MyConnectionString;
con.Open();
OdbcCommand cmds = new OdbcCommand("Select ID from try where kalabaw = 5", con);
OdbcDataAdapter da = new OdbcDataAdapter(cmds);
byte[] image = (byte[])cmds.ExecuteScalar();
TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
Bitmap bitmap = (Bitmap)tc.ConvertFrom(image);
pictureBox2.Image = bitmap;
con.Close();
I totally stole this answer from convert array of bytes to bitmapimage
public BitmapImage ToImage(byte[] array)
{
using (var ms = new System.IO.MemoryStream(array))
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
return image;
}
}
try the below. I expect it should work.
byte[] bytes = GetBytesArrayFromDatabase();
using (MemoryStream ms = new MemoryStream(bytes))
{
ms.Position = 0;
Bitmap obj = new Bitmap(ms);
// use the Bitmap object `obj` here
}
I am working on Extracting images from a database(SQL Server 2008)
I im using a WPF application , using the Grid_Loaded event to load pages.
The examples i have found are mainly using the combo box to select the ID of an image and displaying it. However i do not want to use the combo box.
I have a few lines of code i have worked on and found on the internet, will be appreciated if someone helps me!
private void LoadImages()
{
try
{
string connstr = #"Server=CTGPJLPC21\SQLEXPRESS;Database=testing;Trusted_Connection=True;";
using (SqlConnection conn = new SqlConnection(connstr))
{
conn.Open();
using (SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM test_table", conn))
{
dset = new DataSet();
adapter.Fill(dset);
}
DataTable dt = dset.Tables[0];
foreach (DataRow row in dt.Rows)
{
if (dset.Tables[0].Rows.Count == 1)
{
byte[] data = (byte[])dset.Tables[0].Rows[0][0];
MemoryStream strm = new MemoryStream();
strm.Write(data, 0, data.Length);
strm.Position = 0;
System.Drawing.Image img = System.Drawing.Image.FromStream(strm);
BitmapImage bi = new BitmapImage();
bi.BeginInit();
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Seek(0, SeekOrigin.Begin);
bi.StreamSource = ms;
bi.EndInit();
myImg.Source = bi;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
long bufferSize = dataReader.IsDBNull(1) ? 0 : dataReader.GetInt64(1);
if (bufferSize > 0)
{
j.PictureContent = new byte[bufferSize];
bufferSize = dataReader.GetBytes(1, 0, j.PictureContent, 0, Convert.ToInt32(bufferSize));
}
Where j is a class instance and j.PictureContent is a byte[].