如何在使用XAML获取图像资源后释放/缓存它们

4

我目前在一个使用Prism 6的WPF项目中工作,尽量避免使用代码后台。我在ViewModel中有一个变量,其中包含所讨论的图像存储的本地路径。在View中,我将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个回答

1
如果您在调用 MainWindow 或 UserControl 的 InitializeComponent 之前可以分配 DataContext,则可以在 XAML 中明确创建 BitmapImage,并将其 CacheOption 设置为 OnLoad。框架将立即加载文件而不保持打开状态。
<Image>
    <Image.Source>
        <BitmapImage UriSource="{Binding ImageUri}" CacheOption="OnLoad"/>
    </Image.Source>
</Image>

如果在InitializeComponent之后必须设置DataContext或ImageUri属性,则可以添加一个类型为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}"/>

1
你可以编写一个转换器来将文件名转换为字节。
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}}"/>

在我看来,这是最好的答案。它将其保留在XAML中并分离关注点。 - GeorgiG

1
你不需要在你的ViewModel中创建一个ImageSource对象。相反,只需创建一个byte[]类型的属性,并使用标准IO函数从文件中设置该属性。然后,您可以将Image控件的Source属性绑定到此字节数组 - 该控件将自动处理转换为可显示的图像。
请参见我的答案this question以了解异步处理此操作的方法。
一旦读取文件,您可以根据需要删除它。如果图像来自某些外部来源,例如网络下载,则可以直接使用数据,而无需将其保存到磁盘上。

-1
只要您的ImageSource指向本地磁盘上的文件,就无法删除它。我想到了以下两种方法:
  1. 既然您有实际图像的路径,为什么不将其复制到TEMP文件夹并绑定到此路径呢?这样,您永远不会引用现在可以被删除的实际图像。

  2. 使用/构建一个用于图像文件的http主机(CMS)(例如imgur,StackOverflow也使用它)。始终在此保存/托管文件。这样,ImagesSource就是http uri。


这是错误的。有很多方法可以立即加载图像,以便在显示后直接删除该文件。 - Clemens
@Clemens - 我可能错了,但是当你使用Cache选项创建BitmapImage时,它不会在内存中加载图像字节吗?因此,ImageSource不是磁盘上实际的文件,而是来自内存中的字节的图像。 - Prateek Shrivastava
1
它总是加载到内存中。重要的是何时发生这种情况。您可以控制立即发生,因此无需进行任何建议。 - Clemens
如果这是真的,我将尝试基本绑定到磁盘上的图像并保持几分钟。然后将尝试从磁盘中删除该图像。 - Prateek Shrivastava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接