🖼️

【WPF】ImageコントロールにBindingしている画像を削除できるようにする

2023/05/02に公開

WPFの Image コントールで、 Source プロパティに画像ファイルパスをバインドすることで、画像ファイルを画面表示しようとする。

<Image Source="{Binding Image1Path}" />
public class FooViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    private string? _image1Path = @"C:\path\to\image.png";
    public string? Image1Path
    {
        get => _image1Path;
	set
	{
	    _image1Path = value;
	    // Raise PropertyChanged
	    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Image1Path)));
	}
    }
}

Source プロパティは ImageSource 型だが、stringのまま画像ファイルのパスをBindingしても、勝手にコンバータで変換してくれる。

ところが、このBindingされた状態で画像ファイルを File.Delete() で削除しようとすると、以下のようなExceptionになる。

System.IO.IOException: 'The process cannot access the file 'C:\path\to\image.png' because it is being used by another process.'

これを防ぐには、画像を表示するときに元のファイルをロックしないように、コンバータを自作する。

    [ValueConversion(typeof(string), typeof(ImageSource))]
    public class NoLockImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var filePath = value as string;
            if (string.IsNullOrEmpty(filePath))
            {
                return Binding.DoNothing;
            }

            using (var fs = new FileStream(filePath, FileMode.Open))
            {
	        // OnLoadにする
                var decoder = BitmapDecoder.Create(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
                var bmp = new WriteableBitmap(decoder.Frames[0]);
                bmp.Freeze();
                return bmp;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
<Grid >
    <Grid.Resources>
	<!-- 親コンテナなどでリソース定義しておく
       (Window で xmlns:local="clr-namespace:FooNamespace" のように名前空間を定義する) -->
        <local:NoLockImageConverter x:Key="noLockImageConverter" />
    </Grid.Resource>
    <Image Source="{Binding Image1Path, Converter={StaticResource noLockImageConverter}}" />
</Grid>

これで表示時にメモリ上に画像をロードするようになるため、元ファイルを削除することもできるようになる。

Discussion