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();
}
}
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.
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;
You can solve this problem directly from xaml.
<Image>
<Image.Source>
<BitmapImage
CacheOption="OnLoad"
UriSource="{Binding FileName}" />
</Image.Source>
</Image>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With