Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wpf Image Control blocks the file

Tags:

c#

image

wpf

I have a simple Window with button and second Window is opened when I click on the Button. Second Window has a Image control, which displays a .png-file. So if I use FileObject property for Binding all is OK, I can delete file from File Explorer. But if I use FileName property for Binding I cannot delete file from File Explorer, I get OS exception. I cannot do this even if I close second window, even if I invoke GC explicitly. What is the problem with FileName property? Any ideas?

Win 7, Net 4.0

Window1

<Grid>
    <Button Content="Ok"
            Width="100" 
            VerticalAlignment="Center" 
            HorizontalAlignment="Center" 
            Click="Click" 
            Padding="0,2,0,2" 
            IsDefault="True" 
            Name="_btnOk"/>
</Grid> 



public partial class Window : Window
{

    public Window()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void Click(Object sender, RoutedEventArgs e)
    {
        var window = new Window3();
        window.ShowDialog();
    }
}

Window2

<Grid>
    <Image Source="{Binding FileObject}"></Image>
</Grid>

public partial class Window2 : Window
{
    public Window2()
    {
        InitializeComponent();
        DataContext = this;
        FileName = "D:/pdf/myfile.png";

        Closing += Window2_Closing;
    }

    public String FileName { get; set; }

    public Object FileObject
    {
        get
        {
            if (String.IsNullOrEmpty(FileName))
                return null;

            if (!File.Exists(FileName))
                return null;

            var ms = new MemoryStream();
            var bi = new BitmapImage();

            using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
            {
                fs.CopyTo(ms);
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.EndInit();
            }
            return bi;
        }
    }

    void Window2_Closing(Object sender, System.ComponentModel.CancelEventArgs e)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}
like image 423
monstr Avatar asked Apr 14 '15 10:04

monstr


3 Answers

When you bind the Image.Source property to an Uri (or a string, from which an Uri is created internally), WPF uses a built-in type converter that creates a BitmapFrame from the Uri.

If the Uri contains a path to a local file, the BitmapFrame keeps the file open, as long as it is existing. This may be longer than it is actually used in your application, because it may by cached by WPF.

When you need to be able to delete the file that an image was loaded from, you should always use your FileObject approach, but it should look like this:

public ImageSource Image
{
    get
    {
        ...
        var bi = new BitmapImage();
        using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
        {
            bi.BeginInit();
            bi.CacheOption = BitmapCacheOption.OnLoad;
            bi.StreamSource = fs;
            bi.EndInit();
        }
        return bi;
    }
}

Or like this:

public ImageSource Image
{
    get
    {
        using (var fs = new FileStream(FileName, FileMode.Open, FileAccess.Read))
        {
            return BitmapFrame.Create(
                fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
        }
    }
}

Or you bind to the FileName property with a binding converter that creates a BitmapImage or BitmapFrame as shown above.

like image 188
Clemens Avatar answered Nov 07 '22 21:11

Clemens


use this code, I will explain later what is the problem.

var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.UriSource = new Uri(FilePath);
image.EndInit();
return image;
like image 32
Mujahid Daud Khan Avatar answered Nov 07 '22 23:11

Mujahid Daud Khan


You can solve this problem directly from xaml.

<Image>
    <Image.Source>
        <BitmapImage
            CacheOption="OnLoad"
            UriSource="{Binding FileName}" />
    </Image.Source>
</Image>
like image 1
Gabriel Ferrer Avatar answered Nov 07 '22 22:11

Gabriel Ferrer