使用 XAML 获取图像资源后如何 release/caching

How to release/caching image resources after get them using XAML

目前我在一个使用 prism 6 的 WPF 项目中工作,尽可能避免代码隐藏。我在 ViewModel 中有一个变量,其中包含存储相关图像的本地路径。在视图中,我将图像控件的源 属性 绑定到 ViewModel 的变量,我可以显示图像。

当图像仍在视图中显示时我需要从磁盘中删除图像时出现问题。然后,如果我这样做,我会得到典型的“Image is in use”。我在论坛上阅读了有关图像缓存的内容,我想知道我是否可以在这种情况下应用它来避免这种行为,如果可能的话只使用 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>

只要您的 ImageSource 指向本地磁盘上的文件,您就无法删除它。我想到了以下两种方法:

  1. 既然你有实际图像的路径 - 为什么不将它复制到 TEMP 文件夹并绑定到这个路径。这样你就永远不会引用现在可以删除的实际图像。

  2. Use/Build 图像文件(如)imgur(Whosebug 也使用)的 http 主机 (CMS)。总是 save/host 个文件。这样 ImageSource 就是一个 http uri.

如果您可以在调用(MainWindow 或 UserControl 的)InitializeComponent 之前分配 DataContext,则可以在 XAML 中显式创建一个 BitmapImage 并将其 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}"/>

或异步提高 UI 的响应能力:

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

您不需要在 ViewModel 中创建 ImageSource 对象。相反,只需创建类型为 byte[] 的 属性,您可以使用标准 IO 函数从文件中设置它。然后,您可以将 Image 控件的 Source 属性 绑定到此字节数组 - 控件将自动处理到可显示图像的转换。

请参阅我对 的回答以了解以异步方式处理此问题的方法。

读取文件后,您可以根据需要删除它。如果图像来自某些外部来源,例如一个网络下载,你可以直接使用数据,甚至根本不需要将它保存到磁盘。

您可以为此编写一个转换器,将文件名转换为字节

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}}"/>