Delete image created by Interop Powerpoint in WPF Application - wpf

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;

Related

Threading WriteableBitmap to Image control

I am using OpenGL to render a static picture in WPF, on a .NET Core 3.1 platform. The context is initially displayed in a hidden GLFW window. Then I am using glReadPixels to read the context and display it on an Image control on the UI, using the code shown below. The code works fine when not using threading. However, when calling the rendering code through a different thread the image is not displayed. As an intermediate check, I created the using (FileStream... code, which reads the WriteableBitmap and saves it locally and appears to be working correctly; this means that the WriteableBitmap is created correctly. So, I guess the problem is how to transmit a WriteableBitmap to the Image control, both of which were created on different threads. Dispatcher.BeginInvoke does not work for some strange reason.
byte[] imageBuffer = new byte[imageWidth * imageHeight * 4];
glReadPixels(0, 0, imageWidth, imageHeight, GL_BGRA, GL_UNSIGNED_BYTE, imageBuffer);
imageBuffer = ImageReverse(imageBuffer, imageWidth);
PixelFormat pixelFormat = PixelFormats.Pbgra32;
int rawStride = (imageWidth * pixelFormat.BitsPerPixel) / 8;
BitmapSource bitmapSource = BitmapSource.Create(imageWidth, imageHeight, 96, 96, pixelFormat, null, imageBuffer, rawStride);
WriteableBitmap writeableBitmap = new WriteableBitmap(bitmapSource);
//This is a check whether the writeableBitmap has been created correctly
using (FileStream fileStream = new FileStream(#"C:\Temp\Image1.bmp", FileMode.Create))
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
encoder.Save(fileStream);
}
Action action = new Action(() => {
_imgSoilProfile.Source = writeableBitmap;
});
_imgSoilProfile.Dispatcher.BeginInvoke(action, DispatcherPriority.Send);
Try to call w.Freeze() on the background thread before you set the Source of the Image:
using (FileStream fileStream = new FileStream(#"C:\Temp\Image1.bmp", FileMode.Create))
{
...
}
w.Freeze();
Action action = new Action(() => {
_imgSoilProfile.Source = writeableBitmap;
});
From the docs:
A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot.

WPF Image Caching

I have a WPF application that takes a snapshot image from a video file. The user can define the timestamp from which to take the image. The image is then saved to a temporary location on disk, and is then rendered into an <image> element.
The user should then be able to select a different timestamp, which then overwrites the temporary file on disk - this should then be displayed within the <image> element.
Using Image.Source = null;, I can clear the image file from the <image> element, so it displays a blank space instead. However, if the source image file is then overwritten with a new image (with the same name) and loaded into the <image> element, it still shows the old image.
I am using the following logic:
// Overwrite temporary file file here
// Clear out the reference to the temporary image
Image_Preview.Source = null;
// Load in new image (same source file name)
Image = new BitmapImage();
Image.BeginInit();
Image.CacheOption = BitmapCacheOption.OnLoad;
Image.UriSource = new Uri(file);
Image.EndInit();
Image_Preview.Source = Image;
The image displayed in the <image> element does not change, even though the original file has been completely replaced. Is there an image caching issue here that I am not aware of?
By default, WPF caches BitmapImages that are loaded from URIs.
You can avoid that by setting the BitmapCreateOptions.IgnoreImageCache flag:
var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(file);
image.EndInit();
Image_Preview.Source = image;
Or you load the BitmapImage directly from a Stream:
var image = new BitmapImage();
using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read))
{
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
}
Image_Preview.Source = image;

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.

Problems displaying an image in Silverlight

I am trying to generate an image in silverlight but for some reason it is not showing.
Here is the code that I am using
Image fireBall = new Image();
Uri imageUri = new Uri("/Yambushi;Images/Projectiles/Fireball.png", UriKind.Relative);
fireBall.Source = new BitmapImage(imageUri);
cnvGame.Children.Add(fireBall);
fireBall.SetValue(Canvas.LeftProperty, Convert.ToDouble(100)); //(Doll left property)
fireBall.SetValue(Canvas.TopProperty, Convert.ToDouble(100));
fireBall.Height = 30;
fireBall.Width = 30;
Yambushi being the solution name and cnvGame the canvas I want to display the image in. The png file build action is set to content
Ok solved the problem...after I debugged the sourceUri of an image i've implemented through xaml and the source had to be as follows:
Uri imageUri = new Uri("/Images/Projectiles/Fireball.png", UriKind.Relative);
fireBall.Source = new BitmapImage(imageUri);

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