18

Am loading image like below

XAML

<Image Stretch="None" Grid.Row="16" Height="70" HorizontalAlignment="Left" Name="imgThumbnail" VerticalAlignment="Top" Width="70" Grid.RowSpan="3" Margin="133,1,0,0" Grid.Column="2" Grid.ColumnSpan="2" />

CodeBehind

if (Path.GetFileNameWithoutExtension(filePath).ToLower().Contains(slugName.ToLower() + "_70x70"))
{
    imgThumbnail.BeginInit();
    imgThumbnail.Stretch = Stretch.UniformToFill;
    imgThumbnail.Source = new BitmapImage(new Uri(filePath));
    imgThumbnail.EndInit();
    count = count + 1;
}

Above code work fine , now I have a delete button next to my thumbnail, if delete button called I suppose to delete all the images from the source location.

Here is the code to delete the image files

internal int Remove(string slugName, DirectoryInfo outputFolder)
{
    Helper.MetadataView.imgThumbnail.Source = null;

    foreach (string filePath_ToBeDeleted in filePathList_ToBeDeleted)
    {
        if (File.Exists(filePath_ToBeDeleted))
        {
            Helper.MetadataView.imgThumbnail.IsEnabled = false;
            File.Delete(filePath_ToBeDeleted);
            count += 1;
            }
        }
        return count;
    }
    return 0; // slugName == null
}

I tried to source to be null and delete, but it throws exception like below

The process cannot access the file '\serv1\Dev\Images\730_Test4_0406_70x70.jpg' because it is being used by another process.

Am not sure how to dispose, please someone guide me.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Usher
  • 2,146
  • 9
  • 43
  • 81

2 Answers2

48

You should not use that Image directly in your application if you want to delete or move it.

imgThumbnail.Source = new BitmapImage(new Uri(filePath));

Instead, do this:

BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filePath);
image.EndInit();
imgThumbnail.Source = image;

For more read this

Community
  • 1
  • 1
Nikhil Agrawal
  • 47,018
  • 22
  • 121
  • 208
  • 1
    This is nice solution. instead of creating new `Image` I used `ImageSource`. Value assign just like `ImageSource SomeImageSource = image` and binding in xaml ` `. Also wrapped that code inside of a method. – Mr. Blond Jul 24 '15 at 08:21
  • 1
    I had done everything but set the CacheOption property to OnLoad. The default is OnDemand which keeps the Source stream open. It seems OnLoad should be the default in order to behave as expected. Full docs on this are found here: https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.cacheoption%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396 – Dan Randolph Oct 19 '16 at 02:13
  • This worked for me with Binding. I created a new property that uses this code to convert the file path string to an image, then I bound my WPF image to the image as shown above. Worked great! – seabass2020 Nov 02 '16 at 18:45
  • Here's a solution for data binding scenarios: http://stackoverflow.com/questions/16908383/how-to-release-image-from-image-source-in-wpf – Chris Bordeman Nov 16 '16 at 16:17
  • @seabass2020 sorry lost the correct link but make an IValueConverter that does this and use on binding: string path = value as string; if (path != null && System.IO.File.Exists(path)) { // Create new stream and bitmap frame. var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = new FileStream(path, FileMode.Open, FileAccess.Read); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); bitmapImage.StreamSource.Dispose(); return bitmapImage; – Chris Bordeman Dec 04 '16 at 03:25
  • if I use this code is there any changes of Memory increase of the application? – Ankur Tripathi Jun 29 '17 at 06:18
  • @Ankur Tripathi It holds onto the memory as long as is necessary and no longer. – Chris Bordeman Jul 07 '17 at 19:33
  • @ChrisBordeman if i used this code and load 1000 image will increase high level memory ? or memory = size of image X 1000 ? – Ankur Tripathi Jul 10 '17 at 09:54
  • It won't increase memory usage. The stream is immediately destroyed. Do a test if you're unsure. I'm assuming you're paid to do this stuff? – Chris Bordeman Jul 11 '17 at 01:17
  • I spent months trying to know why the image still in use, thanks for the great solution – amal50 May 13 '19 at 23:40
5

To have a good code-reuse a binding converter could be used:

namespace Controls
{
    [ValueConversion(typeof(String), typeof(ImageSource))]
    public class StringToImageSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is string valueString))
            {
                return null;
            }
            try
            {
                ImageSource image = BitmapFrame.Create(new Uri(valueString), BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
                return image;
            }
            catch { return null; }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

And there is a string for binding, for example

public string MyImageString { get; set; } = @"C:\test.jpg"

And in the UI the converter is used, in my case from the Library named "Controls"

<Window x:Class="MainFrame"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="clr-namespace:Controls;assembly=Controls">
    <Window.Resources>
        <controls:StringToImageSourceConverter x:Key="StringToImageSourceConverter" />
    </Window.Resources>
    <Grid>
        <Image Source="{Binding MyImageString, Converter={StaticResource StringToImageSourceConverter}}" />
    </Grid>
</Window>
Coden
  • 2,579
  • 1
  • 18
  • 25