Как освободить / кэшировать ресурсы изображений после получения их с помощью XAML

В настоящее время я работаю в проекте WPF, использующем призму 6, максимально избегая выделенного кода. У меня есть переменная в ViewModel, которая содержит локальный путь, где хранится изображение, о котором идет речь. В представлении я связываю свойство источника элемента управления Image с переменной ViewModel и могу отображать изображение.

Проблема возникает, когда мне нужно удалить изображение с диска, когда оно все еще отображается в представлении. Затем, если я это сделаю, я получу типичное изображение "Используется". Я читал на форуме о кэшировании изображений, и мне интересно, смогу ли я применить его в этом случае, чтобы избежать такого поведения только при использовании XAML, если это возможно.

Я использую этот подход:

<Border Grid.Column="0" BorderThickness="2" BorderBrush="#808080" Height="300" 
        Width="300" Background="#FCFCFC">
    <Image Height="350" Width="350" HorizontalAlignment="Center" 
           VerticalAlignment="Center" Source="{Binding ImageUri}"/>
</Border>

4 ответа

Решение

Вам не нужно создавать ImageSource объект в вашем ViewModel, Вместо этого просто сделайте свойство типа byte[], который вы устанавливаете из файла, используя стандартные функции ввода-вывода. Затем вы можете связать Source собственность вашего Image управление этим байтовым массивом - управление автоматически обработает преобразование в отображаемое изображение.

Посмотрите мой ответ на этот вопрос, чтобы узнать, как справиться с этим асинхронно.

Как только файл будет прочитан, вы можете удалить его при необходимости. Если изображение получено из какого-то внешнего источника, например, из Интернета, вы можете использовать данные напрямую, даже не сохраняя их вообще на диск.

Если вы можете назначить DataContext перед вызовом InitializeComponent (из MainWindow или UserControl), вы можете явно создать BitmapImage в XAML и установить его CacheOption в OnLoad, Фреймворк сразу же загрузит файл и не будет держать его открытым.

<Image>
    <Image.Source>
        <BitmapImage UriSource="{Binding ImageUri}" CacheOption="OnLoad"/>
    </Image.Source>
</Image>

В случае, если DataContext или свойство ImageUri должны быть установлены после InitializeComponent, вы можете добавить свойство типа ImageSource

public ImageSource Image
{
    get { return BitmapFrame.Create(
              ImageUri, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); }
}

или же byte[]:

public byte[] Image
{
    get { return File.ReadAllBytes(ImageUri.LocalPath); }
}

и привязать к нему, как:

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

или асинхронно, чтобы увеличить отзывчивость интерфейса:

<Image Source="{Binding Image, IsAsync=True}"/>

Вы можете написать конвертер для этого, чтобы преобразовать имя файла в байты

public class FilenameToBytesConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return null;
        }
        var path = (string)value;

        if (!File.Exists(path))
        {
            return null;
        }

        return File.ReadAllBytes(path);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

и использовать его в представлении

<Window ...
        xmlns:Converters="clr-namespace:YourNamespacePath.Converters"/>

    <Window.Resources>
        <Converters:FilenameToBytesConverter x:Key="FilenameToBytesConverter"/>
    </Window.Resources>

    <Image Source="{Binding ImageUri, Converter={StaticResource FilenameToBytesConverter}}"/>

Пока у вас есть ImageSource, указывающий на файл на локальном диске - вы не можете его удалить. Следующие 2 подхода приходят мне на ум:

  1. Поскольку у вас есть путь к реальному изображению - почему бы не скопировать его в папку TEMP и привязать к этому пути. Таким образом, вы никогда не ссылаетесь на реальное изображение, которое теперь можно удалить.

  2. Используйте /Build http host (CMS) для файлов изображений (например) imgur (который также использует Stackru). Всегда сохраняйте / размещайте файлы на этом. Таким образом, ImagesSource является http uri.

Другие вопросы по тегам