WPF Keeping Images in Memory - wpf

My WPF UI is keeping all of the images I use in memory. Below is the relevant retention graph. Using the ANTS Memory Profiler 8.7, I have established that none of my code is holding onto any these objects. I have written code so that multiple request for the same image create only one image, but that leaves me with the problem that there are enough images within my application to crash it when they are all loaded simultaneously. I turned off this code when I ran this memory profile. I need to flush these images. I have even resorted to manually calling GC.Collect which did not reduce the memory used. Something is holding these images and it is not my code.
Here is the code for how I expose the BitmapImage to then be bound to Image.Source. This does not contain my image-to-path dictionary caching service that is now turned off.
public BitmapImage Image
{
get
{
var image = new BitmapImage();
image.BeginInit();
image.UriSource = a_url;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.EndInit();
image.Freeze();
return image;
}
}
// The XAML
<Image Source="{Binding Image, Mode=OneWay}"/>
NOT A DUPLICATE
I have determined that the BitmapImage is being held on to and have explicitly invoked GC.Collect which did nothing. The problem is not the GC. I also always Freeze my BitmapImage objects when I create them in code. This is not my first time around.

I can see from the object graph that you have a class called ImageAnimationController, which is holding a reference to the Image control that uses your BitmapImage as its source by a DependencyPropertyDescriptor. The ImageAnimationController uses the DependencyPropertyDescriptor to subscribe to change notifications of the Image control's Source DependencyProperty. When the ImageAnimationController class is disposed it will unsubscribe from the DependencyPropertyDescriptor notifications.
It looks like this ImageAnimationController class is a component of an open source library called WPF Animated GIF. I can't see how you are using this library as you have not included the source, but I imagine that somewhere you have either something like this:
ImageBehavior.SetAnimatedSource(img, image);
Or this:
<Image gif:ImageBehavior.AnimatedSource="{Binding Image}" />
I am not familiar with this library or your code, but I imagine that you will need to ensure that this attached behaviour is correctly detached and disposed of.

Related

How to Serialize an Image Control whose source is set at runtime?

I have a Grid containing several controls, the most important being three image controls.
I need to make a duplicate of this Grid. Serializing by saving the XAML in a MemoryStream doesnt seem to help because, obviously, when I set the source of the Image Control in the code behind at runtime, this change is not reflected in the XAML designer code. [Technically it is, but as
<Image.Source> System.Windows.Interop.InteropBitmap</Image.Source>
and I get some wierd exception]
So, how can I serialize my Image control?
More generally, how can I Clone my Grid control to reflect any changes to the UI that happened after the window loaded?
If I understood your question properly, I think you just need to create a clone of your grid at runtime. If so take a look at these threads
How can you clone a WPF object?
http://social.msdn.microsoft.com/Forums/en-HK/wpf/thread/e1a63ed2-a432-4c46-8f3b-4f172702cd7c
Use this function to clone an Object
public static T DeepClone<T>(T from)
{
using (MemoryStream s = new MemoryStream())
{
BinaryFormatter f = new BinaryFormatter();
f.Serialize(s, from);
s.Position = 0;
object clone = f.Deserialize(s);
return (T)clone;
}
}
above function was by Arcturus

MVVM, Threading and Image Source DataBinidng

I have an MVVM implementation in which I have a WPF ListBox that will contain a collection of child WPF Image controls. The Source for each control may change as often as 3 times per second. When I have just a single image in my list, life is grand and my app is responsive. When I start having 4 or 5 my application child images, my app starts grinding, I should also mention that I have to a Bitmap to BitmapSource conversion for each new and or updated image that I have.
How should I update my children controls Source property while keeping my application as responsive as possible?
Here is the current code in my ViewModel:
public BitmapSource CameraBitmapSource
{
get
{
Application.Current.Dispatcher.BeginInvoke((Action)delegate
{
BuildImageSource();
}, DispatcherPriority.Background);
return this.cameraBitmapSource;
}
}
BuildImageSource() is where I fetch my new bitmap and convert to a BitmapSource and then assign to my private cameraBitmapSource object.
Because you use Dispatcher.BeginInvoke, you're doing all the work on the UI thread, which makes your app unresponsive. You should build the images on a separate thread. The easiest way to do that is to make the binding asynchronous, and call your BuildImageSource method directly.
ViewModel
public BitmapSource CameraBitmapSource
{
get
{
BuildImageSource();
return this.cameraBitmapSource;
}
}
XAML
<Image Source="{Binding CameraBitmapSource, IsAsync=True}" />
Just remember to Freeze the ImageSource in BuildImageSource so that it can be used on the UI thread (DependencyObjects can only be used on the thread that created them, unless they're Freezable and frozen)

Threading with WPF Images (System.InvalidOperationException)

I'm using a thread to get an image from a website and shoot it back to the parent form (WPF) to display. I ran into an issue and have managed to debug it to this example:
public void Watch()
{
while (true)
{
Bitmap bmp = new Bitmap(1, 1);
BitmapImage bmpImg = new BitmapImage();
this.SetImage(bmp, bmpImg);
}
}
public delegate void SetImageCallback(Bitmap bmp, BitmapImage bmpImg);
private void SetImage(Bitmap bmp, BitmapImage bmpImg)
{
if (!this.imgVideo.Dispatcher.CheckAccess())
{
SetImageCallback del = new SetImageCallback(SetImage);
this.Dispatcher.Invoke(del, bmp, bmpImg);
}
else
{
Bitmap bitmap = bmp;
BitmapImage bitmapImage = bmpImg;
}
}
Keep in mind that Watch() runs on its own thread. If I use the bitmap object (which I can use with PictureBox in Window Forms) everything works great. That is, debugging this code, when I get to the line
Bitmap bitmap = bmp;
And inspect the variable bmp, everything is great and works as expected. HOWEVER, when I get to the next line
BitmapImage bitmapImage = bmpImg;
And inpsect the variable bmpImage, I get a ton of System.InvalidOperationException's. When this is in practice and gets assigned to a WPF Image object, it says that "The calling thread cannot access this object because a different thread owns it." Why am I running into this issue with WPF BitmapImages (which are required to set an ImageSource) but NOT in Windows Forms Bitmap objects (which can be used to set a PictureBox)? How do I fix this in WPF?
Most objects in WPF are of this category: they cannot be shared between different threads. However certain low-level resources such as brushes and bitmaps are derived from a special class called Freezable that if frozen can be shared between different threads. Of course once an object is frozen is can no longer be modified in any way. To freeze a freezable object simply call Freeze and this will prevent cross-thread exceptions.
Instead of
if (!this.imgVideo.Dispatcher.CheckAccess())
{
SetImageCallback del = new SetImageCallback(SetImage);
this.Dispatcher.Invoke(del, bmp, bmpImg);
}
try using :
if (!App.Current.Dispatcher.CheckAccess())
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<CustomObject>(SetImage),CustomeObjectInstance );
Here Cutom object will be a wrapper class wrapping
Bitmap bmp, BitmapImage bmpImg
Obviously, your SetImage signature will change to
SetImage(CutomObject custObj)
I have not tested the code but this may solve the issue.
Let us know if this works so that some poor soul can be benefitted from this post.
All the best!
Sid

WPF Image Formats and Binding

I have a project which requires be to convert an in memory System.Drawing.Bitmap into an in memory BitmapImage so I can bind the BitmapImage to an Image control via XAML. The problem I am running into is during the conversion the original bitmap gets shrunk and is very blurry.
This is very bad because the original bitmap is a bar code and I need this bar code to stay readable. Is there anything I can do to preserve the integrity of my bitmap? Or is it possible to bind a System.Drawing.Bitmap to an WPF Image control without first saving the bitmap and using a URI?
Also can anyone explain to me the difference between all these image formats? It seems as if there are a ton of them, they reside in numerous namespaces and it is a pain to convert between them.
EDIT.....
public static BitmapImage GetBitmapImage(Bitmap bmp)
{
MemoryStream ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Bmp);
BitmapImage bmpI = new BitmapImage();
bmpI.BeginInit();
bmpI.StreamSource = ms;
bmpI.EndInit();
ms.Close();
ms.Dispose();
return bmpI;
}
Have you tried different bitmap scaling modes?
For example:
Image RenderOptions.BitmapScalingMode="HighQuality" ...
In my opinion, the easiest way to get rid of all those burdens is to create a user control, then put a
WindowsFormsHost
on to that control. After that you put a windows forms image to the host, and in the underlying code of the user control, you can create a dependency property to bind data and update changes to the image control.
Cheers.

Deleting a window's background image WPF

I'm having a problem in WPF where a window doesn't release it's file-lock on the background image file after closing, before another part of the application tries to write to the image.
So as an example; say I have a WPF app consisting of 3 windows, 1 "menu" selection window and 2 others. Both of the windows create an ImageBrush using a BitmapImage as the ImageSource (the same image).
Window A has a button that when pressed, cycles through the available background images by copying them each over the file used as the original ImageSource and creating a new ImageBrush and setting the Window.Background to the new brush.
Window B simply uses the ImageBrush to draw the Window.Background.
If Window A is launched, backgrounds switched, closed and then Window B launched, everything is fine.
If Window B is launched, closed, then Window A is launched and backgrounds switched it crashes. Trying to switch the backgrounds throws an IOException because:
"The process cannot access the file 'C:\Backgrounds\Background.png' because it is being used by another process."
So Window B must still be holding onto it somehow!? I have tried doing a GC.Collect(); GC.WaitForPendingFinalizers(); to see if that cures the problem but it doesn't.
The answer Thomas gave is correct, and works well if you have a file path, don't want to cache the bitmap, and don't want to use XAML.
However it should also be mentioned that BitmapImage has a built-in way to load the bitmap immediately by setting BitmapCacheOption:
BitmapImage img = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad };
img.BeginInit();
img.UriSource = imageUrl;
img.EndInit();
or
<BitmapImage CacheOption="OnLoad" UriSource="..." />
This will load the bitmap immediately and explicitly close the stream, just as using a FileStream would, but with several differences:
It will work with any Uri, such as a pack:// Uri.
It can be used directly from XAML
The bitmap is cached in the bitmap cache, so future use of the same Uri won't go to the disk. In your particular application this may be a bad thing, but for other uses it may be a desirable feature.
I assume you're loading the image directly from the file, like that ?
BitmapImage img = new BitmapImage();
img.BeginInit();
img.UriSource = imageUrl;
img.EndInit();
Try to load it from a stream instead ; that way you can close the stream yourself after the image is loaded, so that the file isn't locked :
BitmapImage img = new BitmapImage();
using (FileStream fs = File.OpenRead(imageFilePath)
{
img.BeginInit();
img.StreamSource = fs;
img.EndInit();
}

Resources