5

I am displaying some image in my wpf app using following code:

 <Image Source="{Binding Path=TemplateImagePath, Mode=TwoWay}"  Grid.Row="3" Grid.Column="2"  Width="400" Height="200"/>

and setting it's binding property inside code behind's constructor by navigating through some directory, below is the code:

DirectoryInfo Dir = new DirectoryInfo(@"D:/Template");
            if (Dir.Exists)
            {
                if (Dir.GetFiles().Count() > 0)
                {
                    foreach (FileInfo item in Dir.GetFiles())
                    {
                        TemplateImagePath = item.FullName;
                    }
                }
            }

but if user upload some other image then I need to delete this old image which is I am doing in the following way and setting image binding to null:

DirectoryInfo Dir = new DirectoryInfo(@"D:/Template");
                if (Dir.Exists)
                {
                    if (Dir.GetFiles().Count() > 0)
                    {
                        foreach (FileInfo item in Dir.GetFiles())
                        {
                            TemplateImagePath= null;
                            File.Delete(item.FullName);
                        }
                    }
                }

But Iam getting exception that Cannot delete file used by some other process. How can I delete it?

H.B.
  • 166,899
  • 29
  • 327
  • 400
SST
  • 459
  • 3
  • 20
  • 35
  • Did you try not using a TwoWay Binding? Another solution that could work is to not directly setting a path, but creating a BitmapImage from the path and binding to that bitmap image. – Akku Oct 09 '12 at 12:34
  • how to do that. I am newbie in WPF.any code example – SST Oct 09 '12 at 12:37
  • Sorry, no time, use Google please. – Akku Oct 09 '12 at 12:38

2 Answers2

12

In order to be able to delete the image while it is displayed in an ImageControl, you have to create a new BitmapImage or BitmapFrame object that has BitmapCacheOption.OnLoad set. The bitmap will then be loaded from file immediately and the file is not locked afterwards.

Change your property from string TemplateImagePath to ImageSource TemplateImage and bind like this:

<Image Source="{Binding TemplateImage}"/>

The set the TemplateImage property like this:

BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(item.FullName);
image.EndInit();
TemplateImage = image;

or this:

TemplateImage = BitmapFrame.Create(
    new Uri(item.FullName),
    BitmapCreateOptions.None,
    BitmapCacheOption.OnLoad);

If you want to keep binding to your TemplateImagePath property you may instead use a binding converter that converts the string to an ImageSource as shown above.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • 3
    Also I need to add 'System.GC.Collect(); System.GC.WaitForPendingFinalizers();' before deleting that file that is before this line: File.Delete(item.FullName) – SST Oct 10 '12 at 04:55
  • Spent ages on this, but adding 'GC.Collect();' and 'GC.WaitForPendingFinalizers();' before deleting solved my issue. Thanks @SST – Sandwich Aug 11 '16 at 09:37
  • @Jon You may also want to take a look at this: http://stackoverflow.com/q/13262548/1136211. While triggering the GC might work, the "correct" (i.e. documented) solution would be to set the StreamSource instead of the UriSource property. – Clemens Aug 11 '16 at 09:42
  • @Clemens Thanks, I'll take a look – Sandwich Aug 12 '16 at 08:10
  • How you do this with video ? – Altiano Gerung Sep 24 '18 at 05:11
0

According to Clemens suggestion, here is the binding converter to have a good code-reuse:

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