How to save a WPF BitmapSource image to a file? - wpf

In WPF, the System.Windows.Clipboard.getImage() function returns a BitmapSource object. As a newbie in WPF coming from a WinForms background, its not clear to me how to save this image to a file. What are the steps I must take?

You need to use an encoder (subclass of BitmapEncoder). For instance, to save it to the PNG format, you do something like that :
public static void SaveClipboardImageToFile(string filePath)
{
var image = Clipboard.GetImage();
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(fileStream);
}
}
By the way, note that there's a bug in Clipboard.GetImage. It shouldn't be a problem if you just save the image to a file, but it will be if you want to display it.
EDIT : the bug mentioned above seems to be fixed in 4.0

This clears up the BitmapFrame.Create issue that you have.
public static void SaveClipboardImageToFile(string filePath)
{
//var image = Clipboard.GetImage();
BitmapSource image = (BitmapSource)Clipboard.GetImage();
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
//encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Frames.Add(BitmapFrame.Create(image As BitmapSource));
encoder.Save(fileStream);
}
}

Related

Delete image created by Interop Powerpoint in WPF Application

I wrote a WPF Application that opens a PowerPoint presentation and save the slides as images to be displayed in a PictureBox. The PictureBox is bounded to ImageBitmap property.
PowerPoint.Application app = new PowerPoint.Application();
PowerPoint.Presentations pres = app.Presentations;
p = pres.Open(FileName, ofalse, ofalse, otrue);
p.Slides[0].Export(ImagePath, "png", 640, 480);
ImageBitmap = new BitmapImage(new Uri(ImagePath));
When the application is closed, I would like to delete all the image files created, but instead I got an error: "“The process cannot access the file because it is being used by another process”.
I have determined (using ProcessExplorer) that the files were being used only by my application.
I read some suggestions that using the code below will allow the image file (located in ImagePath) to be deleted, but I think this will break MVVM since the viewModel now knows about the View.
Image im = GetCopyImage(#"D:\Temp\AAAA.jpg");
myPictureBox.Image = im;
File.Delete(#"D:\Temp\AAAA.jpg");
}
private Image GetCopyImage(string path)
{
using (Image im = Image.FromFile(path))
{
Bitmap bm = new Bitmap(im);
return bm;
}
}
You may set the BitmapCacheOption.OnLoad option during initialization of the BitmapImage. This lets WPF close the image file right after loading the image.
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(ImagePath);
bitmap.EndInit();
ImageBitmap = bitmap;

No image displayed when setting Image.Source to valid data

I'm receiving byte[] image-data (from a webcam source, transmitted over network) and want to display it in my WPF Image control.
If I set the resolution to 160x120, I see a nice tiny image being displayed.
For any other resolution though I see nada, nothing, zilch, squat.
If I write the raw bytes to disk I can see that the data is indeed a valid jpg-image.
Just to make sure it's not jpeg-incompatibility I've tested it with png- and bmp-encoding/decoding as well with the same results; still no image.
Anyone got a bright idea ?
private bool OnImage(byte[] inJpg)
{
this.Dispatch(() =>
{
//File.WriteAllBytes("h:\\tmp\\test" + sImageNum++ + ".jpg", inJpg);
using (MemoryStream ms = new MemoryStream(inJpg))
{
BitmapDecoder decoder = new JpegBitmapDecoder(ms, BitmapCreateOptions.None, BitmapCacheOption.Default);
ImageRemoteVideo.Source = decoder.Frames[0];
}
}
);
}
Assuming ImageRemoteVideo is an instance of a WPF Image control, you may be getting bit by some lazy resolve issues. The Image control has "BeginInit" and "EndInit" methods that you should probably be using. Put the BeginInit method before you set the stream, the other after you set it. Also, if I remember right, the Image class supports a StreamSource. You could try setting your MemoryStream (without disposing of it and without using the JpegBitmapDecoder) to that Property. If that works, then see if you can dispose it after the EndInit call.
I have just found a way to make it work, but I'd love it if someone can explain WHY it works :)
First off, I'm using the raw bytestream to create a BitmapImage.
BUT - if I use a "using" clause around the MemoryStream it doesn't work, if I don't it works.
So this doesn't work :
using (MemoryStream byteStream = new MemoryStream(inJpg))
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.EndInit();
ImageRemoteVideo.Source = image;
}
But this does :
MemoryStream byteStream = new MemoryStream(inJpg);
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.EndInit();
ImageRemoteVideo.Source = image;
Found it. It's like Brannon said a case of delayed loading.
Specifying "CacheOption = BitmapCacheOption.OnLoad;" fixes it !
Cheers !
Working version :
MemoryStream byteStream = new MemoryStream(inJpg);
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = byteStream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
ImageRemoteVideo.BeginInit();
ImageRemoteVideo.Source = image;
ImageRemoteVideo.EndInit();
}

Bitmap to byte[]

I have a problem with saving an image (or a bitmapImage or a PhotoResult) to a byte[] and then converting it back to an image.
I found a lot of posts on the internet about it but they dont work. In this code I got an Unspecifed error when I do this: SetSource ( bitmapImage.SetSource(ms);) and dont know how to do that.
I also want to make a list of Devices (each with a name, id, status and an image which I will represent as a byte[]) and save it to IsolatedStorage, and then read it and list them (with an image of course.)
Here is some code I have so far:
public void photoChooserTask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
imageBytes = new byte[e.ChosenPhoto.Length];
e.ChosenPhoto.Read(imageBytes, 0, imageBytes.Length);
BitmapImage bitmapImage = new BitmapImage();
MemoryStream ms = new MemoryStream(imageBytes);
try
{
bitmapImage.SetSource(ms);
}
catch (Exception ea)
{
//
}
image1.Source = bitmapImage;
}
Have you tried the Microsoft.Phone.PictureDecoder class? It has a DecodeJpeg function that returns an instance of a WritableBitmap object.
Another solution is to use the WritableBitmapEx extension library that makes digital image processing much easier and has a very good performance. The function you need is called FromByteArray.
In both cases you'll have to use a WriteableBitmap, because BitmapImage is protected from modification. Since both BitmapImage and WriteableBitmap are subclasses of BitmapSource, you can easily display them in the image control.
Hope it helps!

WPF application, freeing resources (images)

i have a slideshow application showing some images. I have a simple usercontrol that displays an image. I read the images from an XMl file and each time i create a new instance on a new usercontrol and show it in my application. At some point i need to "refresh" theimages (e.g. new version) i remove all my usercontrols from the application and "null" them - then i try to delete all images on disc. However, this gives me an exception that the resource "myimage.png" is in use by another process and cannot be deleted. What do i have to do to "release" my resources so that i can delete them?
cheers,
I've looked at the WPF source code using .NET Refector and found this answer to a related question that might be a better solution for you. Set the BitmapImage.CacheOption to BitmapCacheOption.OnLoad forces the image data into memory and closes the file immediately.
Using that option, you can delete the from the file system at any time afterwards.
I had a similar problem - I needed to provide an image preview using a temporary file for the image. Once the preview was closed I wanted to delete the file.
I used an explicit stream instead of a URI to load the image:
BitmapImage imageSource = new BitmapImage();
FileStream imageStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
imageSource.BeginInit();
imageSource.StreamSource = imageStream;
imageSource.EndInit();
// imagePreview is a WPF Image
imagePreview.Source = imageSource;
Then when it was time to close the UI and release the image I explicitly closed the stream:
BitmapImage imageSource = imagePreview.Source as BitmapImage;
imagePreview.Source = null;
if (null != imageSource)
{
System.IO.Stream stream = imageSource.StreamSource;
imageSource.StreamSource = null;
if (null != stream)
{
stream.Close();
}
// now the file can be deleted
File.Delete(filePath);
}
I used an explicit stream instead of a URI to load the image:
BitmapImage imageSource = new BitmapImage();
FileStream imageStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
imageSource.BeginInit();
imageSource.StreamSource = imageStream;
imageSource.EndInit();
// imagePreview is a WPF Image
imagePreview.Source = imageSource;
Then when it was time to close the UI and release the image I explicitly closed the stream:
BitmapImage imageSource = imagePreview.Source as BitmapImage;
imagePreview.Source = null;
if (null != imageSource)
{
System.IO.Stream stream = imageSource.StreamSource;
imageSource.StreamSource = null;
if (null != stream)
{
stream.Close();
}
// now the file can be deleted
File.Delete(filePath);
}
this solution is working fine. thanks.

How to render an <image> in a background WPF process?

I'm taking Silverlight XAML and sending it to a web service that renders the XAML in a STA thread and generates a PNG image. All the XAML renders correctly except the <image> entries which 'appear' to load correctly when their Source property is set in WPF but the PNG does not show the referenced image - what am I doing wrong ?
The core of the code that I am using is as below. The value object is a DTO from Silverlight that contains the XAML in the string that ends up as the sXAML local property and the Image URI is in the value.ImageURL property.
var canvas = (FrameworkElement)XamlReader.Load(new XmlTextReader(new StringReader(sXAML)));
var obj = canvas.FindName("BGImage");
Image img = null;
if (obj != null)
{
img = obj as Image;
img.ImageFailed += img_ImageFailed;
img.Source = new BitmapImage(new Uri(value.ImageURL, UriKind.Absolute));
}
canvas.Arrange(new Rect(new Size(463d, 381d)));
canvas.UpdateLayout();
var mem = new MemoryStream();
var bmp = new RenderTargetBitmap(463, 381, 96d, 96d, PixelFormats.Pbgra32);
bmp.Render(canvas);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
encoder.Save(mem);
FileStream fs = new FileStream("D:\\out.png", FileMode.Create);
mem.WriteTo(fs);
fs.Close();
NB: The img_ImageFailed event handler is never invoked indicating that the img Source assignment was successful in some way.
Things I would try:
1) In your WPF app, if you 'render' your dynamically loaded Canvas to display in a Window, does it all work (images included)?
2) Have you tried attaching a Loaded handler to the img object to see when the image is actually loaded?
3) Assuming #1 works - where are the images located (are they on the internet/local web server)? If you put a breakpoint in the code on
bmp.Render(canvas);
and wait a while before stepping on - does the image then appear in the rendered output?
I suspect the image is being 'downloaded' asynchronously and you are rendering the Canvas too early, before the Image object has resolved its source.
[UPDATE 29-Jan-09]
possible solution
I copied you code down exactly and gave it a try. When I used a 'local' image location (eg. "c:\images\splash.jpg") the image was rendered fine in the output jpeg. When I used a URL (eg. "http://localhost/images/splash.jpg") it did NOT appear (as you describe).
So I modified your code as follows (to try and FORCE the image to be downloaded - will only work for http: image references) --
if (obj != null)
{
img = obj as Image;
// new stuff
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = getCachedURLStream(new Uri(ImageURL));
bi.EndInit();
img.BeginInit();
img.Source = bi;
img.EndInit();
//img.ImageFailed += img_ImageFailed;
//img.Loaded += new RoutedEventHandler(img_Loaded);
//img.Source = new BitmapImage(new Uri(ImageURL, UriKind.Absolute));
}
public static Stream getCachedURLStream(Uri url)
{
HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(url);
WebResponse response;
webrequest.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
response = webrequest.GetResponse();
Stream s = response.GetResponseStream();
BufferedStream bs = new BufferedStream(s, 8192);
return bs;
}
(the getCachedURLStream method is from synchronous image loading - it's kinda extraneous but I just wanted a quick chunk of code to test) and it seemed to then work (image visible in JPEG). Maybe that will work in your scenario?

Resources