3

My application shows images on screen (images based upon files on the local computer) and users can delete them if needed.

Every time I try to delete a file it results in the following error message:

"The process cannot access the file 'C:\\Users\\Dave\\Desktop\\Duplicate\\Swim.JPG' because it is being used by another process."

I understand the error message.

I have a UserControl which accepts a file path (via a parameter in the constructor) and then binds it to it's (UserControl) DataContext.

As part of debugging this issue I have found the issue is due to setting the DataContext within the UserControl. If I remove this.DataContext = this; from within my UserControl then I can delete the file.

So, my TestUnit looks like

        Ui.UserControls.ImageControl ic = new ImageControl(
           @"C:\Users\Dave\Desktop\Duplicate\Swim.JPG");

        try
        {
            File.Delete(@"C:\Users\Dave\Desktop\Duplicate\Swim.JPG");
        }
        catch (Exception ex)
        {
            Assert.Fail(ex.Message);
        }

The UserControl CodeBehind

    public ImageControl(string path)
    {
        this.FilePath = path;
        this.DataContext = this; // removing this line allows me to delete the file!
        InitializeComponent();
    }

    #region Properties

    private string _filePath;
    public string FilePath
    {
        get { return _filePath; }
        set
        {
            _filePath = value;
            OnPropertyChanged("FilePath");
        }
    }

If it matters, my UserControl XAML is using the 'Image' control, bound to 'FilePath'

I have tried making the UserControl null before deleting, this did not help.

I have tried adding the IDisposible Interface to my UserControl and within the Dispose() method setting this.DataContext = null; but this did not help.

What am I doing wrong? How can I delete this file (or more accurately, make it unused).

Dave
  • 8,163
  • 11
  • 67
  • 103
  • Try setting the FilePath null before deleting. – paparazzo Oct 13 '13 at 15:11
  • Hi @Blam, by setting `this.DataContext =null;` it FilePath is null (FilePath is a property). However, to be really sure, I also tried manually setting it to null and sadly it made no difference. – Dave Oct 13 '13 at 15:14
  • Setting DataContext to null and never assigning it are not the same. – paparazzo Oct 13 '13 at 15:21
  • @Blam, Yes, you're right, that's 100% the problem. I can't find any documentation though to say how to "un-assign" it after is has been assigned (or what is keeping a reference to my FilePath)! I've tried making the properties null manually and via the datacontext, neither worked. – Dave Oct 13 '13 at 15:26
  • Nor, does making the object null! – Dave Oct 13 '13 at 15:35
  • possible duplicate of [Image file copy, is being used by another process](http://stackoverflow.com/questions/18167280/image-file-copy-is-being-used-by-another-process) – Gayot Fow Oct 13 '13 at 16:13

1 Answers1

5

The problem is not the DataContext, but simply the way WPF loads images from files.

When you bind the Source property of an Image control to a string that contains a file path, WPF internally creates a new BitmapFrame object from the path basically like this:

string path = ...
var bitmapImage = BitmapFrame.Create(new Uri(path));

Unfortunately this keeps the Image file opened by WPF, so that you can't delete it.

To get around this you have to change the type of your image property to ImageSource (or a derived type) and load the image manually like shown below.

public ImageSource ImageSource { get; set; } // omitted OnPropertyChanged for brevity

private ImageSource LoadImage(string path)
{
    var bitmapImage = new BitmapImage();

    using (var stream = new FileStream(path, FileMode.Open))
    {
        bitmapImage.BeginInit();
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.StreamSource = stream;
        bitmapImage.EndInit();
        bitmapImage.Freeze(); // optional
    }

    return bitmapImage;
}

...
ImageSource = LoadImage(@"C:\Users\Dave\Desktop\Duplicate\Swim.JPG");
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Thank you for the answer, but as a follow up, how do you know this is what WPF does? There appears to be some magic/witch craft around this community... Did you de-assemble the WPF dll's, or is the WPF code freely available to see this behaviour? – Dave Oct 14 '13 at 13:00
  • 1
    Actually I don't know if that is exactly what WPF does. But if you take a debugger and look at the value of the Image.Source property when it is bound to a string, you see that it contains a BitmapFrameDecode instance, which is exactly what BitmapFrame.Create returns. Checking the other way round and calling BitmapFrame.Create with a local file Uri keeps the file open. Of course you could still use a tool like .NET Reflector and disassemble the TypeConverter for ImageSource to see what WPF is really doing. – Clemens Oct 14 '13 at 13:49