Threading with WPF Images (System.InvalidOperationException) - wpf

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

Related

WPF Keeping Images in Memory

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.

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

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();
}

using BitmapSource as Image source in WPF

I'm trying to update an Image (_browserScreenshot below) object in XAML by changing the source image every time an event determines the source needs updating. Right now I have this:
public BitmapSource GetScreen()
{
Bitmap bitmap = new Bitmap(app.Browser.ClientRectangle.Width, app.Browser.ClientRectangle.Height);
app.Browser.DrawToBitmap(bitmap, app.Browser.Bounds);
BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
bitmapSource.Freeze();
bitmap.Dispose();
bitmap = null;
return bitmapSource;
}
Then I have an event handler as shown:
app.BitmapSource.Changed += new EventHandler(BitmapSource_Changed);
void BitmapSource_Changed(object sender, EventArgs e)
{
Window1._browserScreenshot.Source = app.GetScreen();
}
Now whenever this event fires a new screenshot is taken and the source of the Image (called _browserScreenshot here) control should be updated. I keep getting an error about changing the IsFrozen propery, but I can't figure out how to change this correctly and have this work the way I want it to. Thanks in advance everyone.
In all likelyhood you want to Freeze the object. The problem you are having is that you want to create a completely new BitmapSource every time and let the garbage collector dispose of the old image.
The following line turned out to be my problem:
bitmapSource.Freeze();

Resources