I have been having quite a problem with this.
Here is my code.
int frame = 0;
//This is a wpf button event
private void up_Click(object sender, RoutedEventArgs e)
{
frame++;
LoadPic();
}
private void LoadPic()
{
string fn = #"C:\Folder\image" + (frame % 2).ToString() + ".png";
Bitmap bmp = new Bitmap(302, 170);
bmp.Save(fn);
bmp.Dispose();
//Picebox is a wpf Image control
Picbox.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(fn));
}
private void down_Click(object sender, RoutedEventArgs e)
{
frame--;
LoadPic();
}
When I start the program, a wpf window pops open. There are two buttons with the events shown in the code.
When I press the up button twice it works fine. This saves two PNGs to the locations
"C:\Folder\image0.png" and "C:\Folder\image1.png"
The third time I press the button, it should save it to "C:\Folder\image0.png" again.
Instead, it gives the exception 'A generic error occurred in GDI+'.
I have had a similar problem before, and solved it by adding these two lines:
GC.Collect();
GC.WaitForPendingFinalizers();
It didn't work this time.
To avoid the filelock that BitmapImage creates you have to take care of a bit more initialization. According to this question here on SO, it can be done like this (ported to C# from their VB.Net code).
private void LoadPic()
{
string fn = #"C:\Folder\image" + (frame % 2).ToString() + ".png";
Bitmap bmp = new Bitmap(302, 170);
bmp.Save(fn);
bmp.Dispose();
var img = new System.Windows.Media.Imaging.BitmapImage();
img.BeginInit();
img.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
img.UriSource = new Uri(fn);
img.EndInit();
Picbox.Source = img;
}
Related
so i made a simple project where when i click a button the picture edit get an image from a folder file, but when i want to delete the folder that contains the image, it gives me an error. the code as following
private void button1_Click(object sender, EventArgs e)
{
string pathx = AppDomain.CurrentDomain.BaseDirectory + "\\TempImage\\" + "naruto" + ".png";
pictureEdit1.Image = Image.FromFile(pathx);
}
private void button2_Click(object sender, EventArgs e)
{
string dir = AppDomain.CurrentDomain.BaseDirectory + "\\TempImage";
try {
if (Directory.Exists(dir))
{
//////give me an error in here///////
Directory.Delete(dir, true);
}
else
{
MessageBox.Show("folder not found");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
the purpose of this, is in my main project, for cache purpose. so i get an image from a certain folder after coping it from server to local. and when i want to close the main project i need to clear the cache or folder
Update
which is better alternate 1 or alternate 2 (to dispose)
private void button1_Click(object sender, EventArgs e)
{
string pathx = AppDomain.CurrentDomain.BaseDirectory + "\\TempImage\\" + "naruto" + ".png";
//alternate1
using (FileStream stream = new FileStream(pathx, FileMode.Open, FileAccess.Read))
{
pictureEdit1.Image = Image.FromStream(stream);
//stream.Dispose();
}
//alternate2
//Image img = new Bitmap(pathx);
//pictureEdit1.Image = img.GetThumbnailImage(pictureEdit1.Width, pictureEdit1.Height, null, new IntPtr());
//img.Dispose();
}
The documentation on System.Drawing.Bitmap (http://msdn.microsoft.com/en-us/library/0cbhe98f.aspx) says:
The file remains locked until the Bitmap is disposed.
To get around this, you should replace this line:
pictureEdit1.Image = Image.FromFile(pathx);
With this:
Image img = new Bitmap(pathx);
pictureEdit1.Image = img.GetThumbnailImage(pictureEdit1.Width, pictureEdit1.Height, null, new IntPtr());
img.Dispose();
This should load the Bitmap only long enough to create a thumbnail version of the image for use in the PictureBox control, then dispose of it immediately, releasing the lock on the file but still displaying the image on the screen.
Hope this helps!
Edit: Here's the version using using that does the same thing:
using (Image img = new Bitmap(pathx)) {
pictureEdit1.Image = img.GetThumbnailImage(pictureEdit1.Width, pictureEdit1.Height, null, new IntPtr());
}
I am taking bitmap images from camera and store them in a List< Bitmap>.
Within the same class I can set pictureBox images directly from the list like:
pictureBox1.Image = imagesToReturn[0];
where imagesToReturn is a List< Bitmap>.
The problem occurs when I call this method from a different class. ('invalid parameter' error is thrown).
First I return the list from the first object,
then copy it in the second object,
and then destruct the first object.
When I check the returned list I see they are returned correctly (Check the image below; [2] is intentionally null).
Do you guys have any idea what might be the problem or how can I solve this?
Thanks for your valuable time and help.
(edit: long story short, The list you see on the picture above cannot be loaded to pictureboxes because of the 'invalid parameter' error)
Here are some of the codes:
These are in the first class and work perfectly:
/*These are in the first class and works perfectly.*/
public static List<Bitmap> ImagesToReturn = new List<Bitmap>();
public static void updateThumbnails(Bitmap img, int* pictureBoxIndex)
{
switch (*pictureBoxIndex)
{
case 1:
ImagesToReturn[0] = img;
pictureBox2.Image = ImagesToReturn[0];//so that I know they are stored correctly.
pictureBox2.Refresh();
break;
case 2:
ImagesToReturn[1] = img;
pictureBox3.Image = ImagesToReturn[1];
pictureBox3.Refresh();
break;
case 3:
ImagesToReturn[2] = img;
pictureBox4.Image = ImagesToReturn[2];
pictureBox4.Refresh();
break;
case 4:
ImagesToReturn[3] = img;
pictureBox5.Image = ImagesToReturn[3];
pictureBox5.Refresh();
break;
default:
break;
}
*pictureBoxIndex = 0;
img.Dispose();
}
public List<Bitmap> returnCapturedImageList()
{
return ImagesToReturn;
}
//Done button. Terminates the processes.
private void button2_Click(object sender, EventArgs e)
{
keepRunning = false; // in order to stop straming
this.DialogResult = DialogResult.OK;
}
And these are at the second class:
public static List<Bitmap> returnedImages = new List<Bitmap>();
private void button1_Click(object sender, EventArgs e)
{
WindowsFormsApplication6.Form2 cameraModule = new WindowsFormsApplication6.Form2();
var result = cameraModule.ShowDialog();
if (result == DialogResult.OK)
{
returnedImages = cameraModule.returnCapturedImageList()
pictureBox1.Image = returnedImages[0]; //ERROR is thrown here!
pictureBox1.Refresh();
cameraModule.DialogResult = DialogResult.Cancel;
cameraModule.Close();
}
returnedImages.Clear();
}
The issue is solved thanks to the user6144226 and LarsTech. They actually pointed out the mistake.
ImagesToReturn[0] = img; /*not copying or cloning*/
ImagesToReturn[1] = img;
ImagesToReturn[2] = img;
ImagesToReturn[3] = img;
The statements above were not copying image to the list. It was pointing to the 'img'. In other words, when I disposed the 'img' with following command:
img.Dispose();
the "ImagesToReturn[n]" was pointing to the memory that has already been disposed.
The proper way to do it was as follows:
ImagesToReturn[0] = new Bitmap(img); /*copying*/
ImagesToReturn[1] = new Bitmap(img);
ImagesToReturn[2] = new Bitmap(img);
And that solved the issue.
I'm currently trying to update a program by having a button (nextImg) that when clicked calls a function that creates two bitmaps, generates a number starting from 0, and increments every time the nextImg button gets clicked. Then the program takes that number(num) and returns a string from a List[num] which has all the Uri sources of my photos. However when I click next image I get a
System.NullReferenceException.
private void Image_Loaded(object sender, RoutedEventArgs e)
{
// Create a new BitmapImage.
BitmapImage b = new BitmapImage();
BitmapImage b1 = new BitmapImage();
Image img = new Image();
string sSrc = numGen();
b.BeginInit();
b.UriSource = new Uri(sSrc);
b.EndInit();
b1.BeginInit();
b1.UriSource = b.UriSource;
b1.EndInit();
img = sender as Image;
img.Source = b; // Error on this line
Canvas1.Height = Picturebox1.Height;
Canvas1.Width = Picturebox1.Width;
Canvas1.Margin = Picturebox1.Margin;
}
I had some trouble wording it in the title of this post so please looking here if you are confused on my question.
In the instance my question exists, my image viewer is the default for .jpg files. How would I go about setting the image of the picturebox to the .jpg file that was clicked?
I've researched a bit on how to do this but I haven't come up with anything and I believe it's because I'm not wording it right. Thanks in advance, Noah.
Also, if you need any other information or have questions, just ask.
Code:
private void Form1_Load(object sender, EventArgs e)
{
this.Location = new Point(Screen.PrimaryScreen.WorkingArea.Left, Screen.PrimaryScreen.WorkingArea.Top);
this.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
pictureBox1.Size = new Size(this.Width - this.Width/2, this.Height - this.Height/2);
pictureBox1.Location = new Point(300, 250);
pictureBox1.Image = Image.FromFile(Environment.GetCommandLineArgs[0]);
}
EDIT:
Added current code being used
The file that was double clicked will be passed to your application as a "command line argument".
You can retrieve that value using Environment.GetCommandLineArgs() in the Load() event of your form and load it into your PictureBox from there.
The executable itself is at index 0 (zero), with the argument at index 1 (one).
With that in mind, it should look more like this:
private void Form1_Load(object sender, EventArgs e)
{
string[] args = Environment.GetCommandLineArgs();
if (args.Length > 0)
{
try
{
pictureBox1.Image = Image.FromFile(args[1]);
}
catch (Exception ex)
{
MessageBox.Show("File: " + args[1] + "\r\n\r\n" + ex.ToString(), "Error Loading Image");
}
}
}
first i have made an desktop AIR app with drag and drop functions to view the tiles image, but because of some issues, i've tryed to make the same in C#. what do you think is the better choice ? ( i know for this little programm performance is not the question, but i mean the dufficulty and complexity of both )
I was building a simple TileViewer, so a picture is selected in openFileDialog and set as tiled BackgroundImage of the Form.
My problem is:
I've been using a timer ( interval = 500 ) to reload the image, so if i edit the image in photoshop, the TileViewer will automaticaly reload the updated image ( as i save it in photoshop ).
BUT the problem is that photoshop doesnt have premissions to do so, cuz the image is opened in an other programm ( my TileViewer )
I removed the Timer and made a refresh button instead. but its the same problem.
I thought of making a copy of the bitmap data, but then I get an error that I dont have enought memory for a 32px x 32px img.
My code :
private string FileName;
public Form1() {
InitializeComponent();
FileName = "";
}
// OPEN FILE btn
private void button1_Click( object sender, EventArgs e ) {
openFileDialog1.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg";
if(openFileDialog1.ShowDialog() == DialogResult.OK) {
button2.Enabled = true;
FileName = openFileDialog1.FileName;
setImage();
}
}
// REFRESH btn
private void button2_Click( object sender, EventArgs e ) {
if( FileName != "" ) {
setImage();
}
}
private void setImage() {
Bitmap tempImg = new Bitmap( FileName );
Rectangle rect = new Rectangle(0, 0, 100, 100);
PixelFormat format = tempImg.PixelFormat;
this.BackgroundImage = new Bitmap( FileName ).Clone( rect, format );
}
So guys, if you have any suggestions or solutions, let me know.
Update 2:
Another issue was is in line Rectangle rect = new Rectangle(0, 0, 100, 100);
where in the constructor you should pass the width and heigth of the new image not an arbitary value like 100.
thats why the runtime says that you're out of memory !! (It did for me and I have a 18 GB monster machine)
Update:
your're leaking objects hence the out of memory exception
(Haans Already warned you about disposing objects thats.)
Try the following code
private string FileName;
public Form1()
{
InitializeComponent();
FileName = "";
}
// OPEN FILE btn
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "PNG Files (*.png)|*.png|JPG Files (*.jpg)|*.jpg";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
FileName = openFileDialog1.FileName;
setImage();
}
}
// REFRESH btn
private void button2_Click(object sender, EventArgs e)
{
if (FileName != "")
{
setImage();
}
}
private void setImage()
{
Stream str=new FileStream(FileName,FileMode.Open, FileAccess.Read,FileShare.Read);
Bitmap tempImg= new Bitmap(Bitmap.FromStream(str));
str.Close();
using( tempImg)
{
Rectangle rect = new Rectangle(0, 0, tempImg.Width, tempImg.Height);
PixelFormat format = tempImg.PixelFormat;
this.BackgroundImage = new Bitmap(tempImg);
}
}
OLD Answer
Like Jason said
you might need to do somethinglike this in your setimage method
Stream str = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
this.BackgroundImage = Bitmap.FromStream(str);
str.Close();
Note that you're better off closing any stream that you open after you use it.
instead of
private void setImage() {
Bitmap tempImg = new Bitmap( FileName ); <---
Rectangle rect = new Rectangle(0, 0, 100, 100);
PixelFormat format = tempImg.PixelFormat;
this.BackgroundImage = new Bitmap( FileName ).Clone( rect, format );
}
The Bitmap.Clone() method doesn't do what you hope it does. It creates a "shallow" copy. You get a new Bitmap object but it still uses the underlying pixel buffer. Including the memory-mapped file that GDI+ uses to keep the pixel data out of the swap file. Which in turn puts a lock on the file.
You'll need to make a "deep" copy, do so with the Bitmap(Image) constructor. Like this:
private void setImage() {
using (var tempImg = new Bitmap(FileName)) {
this.BackgroundImage = new Bitmap(tempImg);
}
}
The using statement ensures the original image is disposed and the lock gets released.
You will need to open the file manually with ReadOnly flags, and then load the payload of the file into the bitmap by hand.
an alternitive would be to copy the file and then open the copy.
See if you cna run the following code while photoshop has the file open
FileStream s2 = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read);