Load a bitmap to a PictureBox control - c#

This is not working for some reason. I'm not sure why:
objBitmap = new Bitmap(Resource1.im);
Stream stream;
objBitmap.Save(stream, ImageFormat.Bmp);
this.pictureBox2.Image = Image.FromStream(stream);
objBitmap.Dispose();
Basically, I need to show an image in a PictureBox control and I am not sure how to do that.

pictureBox2.Image = objBitmap;

Well, it ought to go kaboom on the Save() method, the stream was never initialized. Not sure what the point of doing this was. There might be one but it isn't visible from your code. The normal version is:
if (this.pictureBox2.Image != null) this.pictureBox2.Dispose();
this.pictureBox2.Image = Properties.Resources.im;
With some question marks about what Resource1 might be. You get my version going by using Project + Properties, Resource tab and click the arrow on the Add Resource button, Add Existing File.

You can change that to
pictureBox2.Image = Resource1.im;
To answer your question, you need to put a stream (probably a MemoryStream) in the stream variable.
You'll also need to "rewind" the stream before reading it back into a Bitmap. (stream.Position = 0)

Related

The dispose of an OpenFileDialog in C#?

I have searched throughout entire Stack Overflow, but I couldn't find an answer to the following:
When I'm using my OpenFileDialog, the files I open get blocked for use out of my program until I close my program. So if I open an image, I am not allowed to replace that image in my Windows Explorer anymore.
I think this is a problem with disposing my OpenFileDialog, but I'm not sure how to solve it...
My code:
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Title = "Open Image";
ofd.Filter = "PNG Image(*.png|*.png" +
"|GIF Image(*.gif|*.gif" +
"|Bitmap Image(*.bmp|*.bmp" +
"|JPEG Compressed Image (*.jpg|*.jpg";
if (ofd.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(ofd.FileName);
}
}
I thought that the using block would solve this problem, but nope... It still gets used by the program. I want to load the image in the picturebox and then I want the image to be available again (so I can rename it, replace it, etc...).
This is not related to the OpenFileDialog. It couldn't possibly be, because the dialog doesn't actually open the file. It just allows the user to select a file to open, and then returns that path to you so that you can write code that opens the file. Besides, you're correctly disposing of the OpenFileDialog through your use of the using statement.
The problem here comes from your actually opening the file—ofd.FileName—as a Bitmap. When you use the Bitmap constructor overload that accepts a path string, the file on disk that contains the image remains locked until the Bitmap object is disposed. So says the documentation:
The file remains locked until the Bitmap is disposed.
Because you're assigning the bitmap to pictureBox1.Image, the Bitmap object will not be disposed until pictureBox1 is disposed. And therefore your image file on disk will remain locked.
If you want to unlock the file, you will either need to make a copy of the bitmap and dispose the original, or clear out the PictureBox and dispose its previous image when you are finished with it.
As I understand your question, it sounds like you want to be able to make changes to the image file on disk while continuing to display the image in the picture box. If that's the case, you will need to make a copy. Do that using the constructor overload that takes an Image, like this:
if (ofd.ShowDialog() == DialogResult.OK)
{
// Load the image the user selected
using (Image img = Image.FromFile(ofd.FileName))
{
// Create a copy of it
Bitmap bmpCopy = new Bitmap(img);
// Clear out the bitmap currently in the picture box,
// if there is one.
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
// Assign the copy of the bitmap to the picture box.
pictureBox1.Image = bmpCopy;
}
}
As written by Chris, try something like:
pictureBox1.Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(old.FileName)));
It reads all the file with File.ReadAllBytes, put it in a MemoryStream and pass the MemoryStream to the Image static initializer.
Equivalent to:
byte[] bytes = File.ReadAllBytes(old.FileName);
MemoryStream ms = new MemoryStream(bytes);
pictureBox1.Image = Image.FromStream(ms);
You mustn't dispose the MemoryStream! If/when the Image will be disposed, the finalizer of MemoryStream will kick in (if you don't have other references to ms) and the MemoryStream will be disposed (note that this isn't something that will happen immediately... It's something that will happen when the GC will run)
The technique I've found to be best is to read the file into a byte array with File.ReadAllBytes() (that opens and closes the file), and then use ImageConverter to convert the byte array into an Image. See here for example: https://stackoverflow.com/a/16576471/253938
Edit:
Quote from that previous post of mine: "Some of the other techniques I've tried have been non-optimal because they changed the bit depth of the pixels (24-bit vs. 32-bit) or ignored the image's resolution (dpi)."

Saving ImageSource (BitmapSource) in XML

I am trying to save and load an ImageSource (or BitmapSource) to and from an XML file. A quick look on SO gave me this answer.
It looked ok so I tried it out but I am getting a strange result.
When I try this code everything works:
BitmapSource testImgSrc = new WriteableBitmap(new BitmapImage(new Uri("pack://application:,,,/MyNameSpace;component/Images/MyImg.png")));
BackgroundImage = testImgSrc;
But when I try this code the image just does not appear at all:
BitmapSource testImgSrc = new WriteableBitmap(new BitmapImage(new Uri("pack://application:,,,/MyNameSpace;component/Images/MyImg.png")));
string testImgStr = ImageToBase64(testImgSrc);
BitmapSource testImg = Base64ToImage(testImgStr);
BackgroundImage = testImg;
There don't seem to be any errors or exceptions. When steping through the code BackgroundImage looks like it gets set to a valid image object.
My WPF form has an image control that has it's source bound to a property that returns the result of the BackgroundImage property. I am guessing the binding is working ok because the first test works as expected.
Can anyone help me to understand why the second test is not displaying my image?
There's a problem with Base64ToImage method from this answer. The documentation states that with the default OnDemand cache option the stream must not be closed before the image is actually used. In your case this means that the Image element is trying to access the already disposed stream.
The fix is pretty simple, you just need to change the cache option to OnLoad and the problem is gone:
BitmapSource Base64ToImage(string base64)
{
byte[] bytes = Convert.FromBase64String(base64);
using (var stream = new MemoryStream(bytes))
{
return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
}

C# - WPF how to unreference a BitmapImage so I can delete the source file?

This seems like a fairly simple issue, but I can't seem to figure a way to work around it.
In a WPF window I have an image, image_small_pic. In the associated C# file I set the value of that using this code:
Uri src = new Uri(image_source, UriKind.RelativeOrAbsolute);
small_image_bmp = new BitmapImage(src);
image_small_pic.Source = small_image_bmp;
Where small_image_bmp is a public BitmapImage object. But then if then, later on, if I change small_image_bmp to another file and reassign image_small_pic.Source, then the original image is still locked and I can't delete it. Even if I try later it's still locked. Any thoughts how I can free this up?
Check out this article. There's some odd behaviour with WPF images that you're coming across. The solution is to read in the bytes yourself and then create an image based on them, since if you let the framework handle it, the file will remain locked.
Uri src = new Uri(image_source, UriKind.RelativeOrAbsolute);
var small_image_bmp = new BitmapImage();
small_image_bmp.BeginInit();
small_image_bmp.CacheOption = BitmapCacheOption.OnLoad;
small_image_bmp.UriSource = src;
small_image_bmp.EndInit();
image_small_pic.Source = small_image_bmp;

How to display a PNG from a file?

I want to switch the image that is displayed on a toolStripButton. But I juste can't find how to do that.
I think it should be something like:
btSearch.Image = new Image("myimage.png");
But it doesn't work ( new Image seems not to exist).
Thank you for your help
Use Image.FromFile():
btSearch.Image = Image.FromFile("myimage.png");
Unfortunately, the file will be locked until you dispose the image. For another solution, see the question, ToolStripButton: what's wrong with assigning an image programmatically.
I recommend the Image.FromStream() method as it doesn't lock the actual file.
For example:
using (var stream = File.OpenRead(path))
using (var image = Image.FromStream(stream))
{
//Black magic here.
}
Note that you must keep the stream open for the lifetime of the Image. The stream is reset to zero if this method is called successively with the same stream.
Here's a previous discussion with an answer from Jon Skeet.

Image.FromStream(PostedFile.InputStream) Fails. (Parameter is not valid.) (AsyncFileUpload))

I'm using an AsyncFileUpload (AJAX Toolkit) to upload images.
I have a Button which handle the image resizing.
This have worked fine for some time, but not anymore...
protected void BtnUploadImage_Click(object sender, EventArgs e)
{
var imageFileNameRegEx = new Regex(#"(.*?)\.(jpg|jpeg|png|gif)$",
RegexOptions.IgnoreCase);
if (!AsyncFileUpload1.HasFile ||
!imageFileNameRegEx.IsMatch(AsyncFileUpload1.FileName))
{
AsyncFileUpload1.FailedValidation = true;
ErrorLabel.Visible = true;
return;
}
ErrorLabel.Visible = false;
var file = AsyncFileUpload1.PostedFile.InputStream;
var img = Image.FromStream(file, false, false);
...
}
Another thing which I find weird: If I try a image which is smaller than 80kb it works..!
We have tried to restart the server, but no change.
Same code runs fine on my machine. (heard that before ?? :) )
I also tried to save the file on the server, then to get the file trough Image.FromFile(), but then I get "Cannot access a closed file."
How to resolve this ?
I would make sure the stream is positioned at the start:
var file = AsyncFileUpload1.FileContent;
file.Seek(0, SeekOrigin.Begin);
var img = Image.FromFile(file);
Second thing to check: the requestLengthDiskThreshold setting. Unless specified this setting has a default of ... yes, 80 KB.
Note: imo there should be no overall difference whether you use Image to read the file stream directly or if you use an intermediate MemoryStream (other than the fact that in the latter case you actually loads the entire file into memory twice). Either way the original file stream will be read from, thus stream position, CAS rights, file permissions, etc still applies.
Note2: and yes, by all means make sure those resources are disposed properly :)
This is correct, it will not work. The problem is that you are crossing a managed/unmanaged boundary, I recently encountered the same. Other problems are that the stream is not directly there and the Image.FromStream has no idea how to deal with it.
The solution is quite straightforward: read everything from PostedFile into a MemoryStream (just use new MemoryStream()) and use the MemoryStream with the Image.FromStream. This will solve your problem.
Make sure to make proper use of using when you work with Image, Graphics and Streams. All of them implement the IDisposable and in an ASP.NET environment, not using using blocks properly, can and will lead to increased memory usage and other nasty side effect on the long run (and ASP.NET apps do run very long!).
The solution should look something like this:
using(Stream memstr = new MemoryStream())
{
// copy to a memory stream
Stream uploadStream = AsyncFileUpload1.PostedFile.InputStream;
byte[] all = new byte[uploadStream.Length];
uploadStream.Read(all, 0, uploadStream.Length);
memstr.Write(all, 0, uploadStream.Length);
memstr.Seek(0, SeekOrigin.Begin);
using(Graphics g = Graphics.FromStream(memstr))
{
// do your img manipulation, or Save it.
}
}
Update: the crossing managed boundary issue only occurs in the reverse (using Response stream), it seems, not with Upload streams, but I'm not entirely sure.

Categories