在Windows 10中处理图像控件的释放

5

注意:通用 Windows 平台 (即 Windows 10 应用,不是 WPF)

我有大约80个图像文件需要在页面内的列表视图中显示。当用户返回到上一页时,我需要释放 Image 控件,以便可以删除这些图像。

问题在于直接绑定图像 uri 会锁定图像文件,并且在返回时不会释放。

我正在使用 MVVMLight

一些代码:

public class FileNameToFullUriConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        string ori = value.ToString();
        string file = ori.Split('/').Last();
        string img = file.Split('.')[0] + ".png";
        img = "ms-appdata:///local/" + StaticClass.ImageFolder + "/" + img;

        return img;

    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return null;
    }
}

和 XAML 相关
<DataTemplate>
    <Grid>
        <Image x:Name="Image2"
               Grid.Column="1"
               HorizontalAlignment="Left"
               Source="{Binding Page2.Page.file,
               Converter={StaticResource FileNameToFullUriConverter},
               Mode=OneWay}"
               Stretch="UniformToFill" />
    </Grid>
</DataTemplate>

我已经尝试过:

  • 将列表设置为null

  • 清空列表(通过调用ListViewName.Clear())

  • 在ViewModelLocator中调用Cleanup

虽然有效,但无法应用的方法: 在ViewModel中,我添加了另一个类型为

ObservableCollection<BitmapImage>

首先,创建一个Collection用于存储图片的路径,然后将ListView绑定到该Collection。

通过这种方式,所有图像都将加载到RAM中,文件不会被锁定,但是会导致一个严重的问题:消耗过多的RAM。我的应用程序使用URI进行绑定时,从100 MB RAM到直接绑定到BitmapImage的900 MB RAM。此外,它需要更长的时间才能加载到页面上,因为它必须在列表完成渲染之前读取和加载所有图像文件到RAM中。

那么,在Windows 10中如何处理Image Control?

PS:此处所说的Image Control是MSDN中的Image类


你能否尝试以 StorageFile 的形式打开这些图像,而不锁定文件本身,然后在你的解决方案中使用它们,而不是 BitmapImage? - Alex Sorokoletov
3个回答

1
你可以在页面的代码后台维护一个Image控件列表。当应用程序返回时,你可以将每个图像的Source属性设置为null:
<DataTemplate>
    <Grid>
        <Image Loaded="Image_Loaded" />
    </Grid>
</DataTemplate>

在代码后台:

private List<Image> _images = new List<Image>();

private void Image_Loaded(object sender, RoutedEventArgs e)
{
    _images.Add(sender as Image);
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);
    foreach(var img in _images)
    {
        img.Source = null;
    }
}

所有锁将被释放。

抱歉,这是我尝试过的方法。它可以工作,但会导致内存泄漏。应用程序从使用100 MB RAM上升到900 MB RAM。 - Hunter Tran
我正在考虑制作一个自定义的图像控件,并在该控件内部处理图像,而不是使用默认的控件。 - Hunter Tran

0

我曾经遇到过同样的问题。我使用了一个解决方法,但不确定这是否是最好的方式。我添加了一个方法来删除已删除的项目,并在每次应用程序启动或列表刷新时调用该方法。

private ObservableCollection<BitmapImage> _items; // your collection which is bound to ListView

// deleting images of removed items
private async Task DeleteUnusedImages()
{
    try
    {
        var folder = await ApplicationData.Current.LocalFolder.GetFolderAsync("folderName");
        var files = await folder.GetFilesAsync(); // getting all files inside that folder

        foreach (var file in files)
        {
            // checking if the image still exist in the collection or got removed
            // if removed then remove it from the local folder too.
            if (!_items.Any(i => i.ImageName.Contains(file.Name)))
            {
                await file.DeleteAsync(StorageDeleteOption.PermanentDelete);
            }
        }
    }
    catch (Exception ex)
    {

    }
}

问题在于当您从绑定到 ListView 的集合中删除项目时,ListView 直到多次刷新后才释放该项目。删除项目后,我尝试手动删除它,进入应用程序的“LocalState”文件夹,但它显示该文件正在被应用程序使用,无法删除。

0

我对图像控件不是很熟悉。在我看来,位图图像必须在删除文件之前被释放。问题在于BitmapImage没有Dispose函数。

我在Stack Overflow上看到其他人询问如何主动释放图像。我猜你需要做同样的事情。

参见: 如何释放BitmapSource

删除BitmapSource文件


谢谢,但这些解决方案适用于不同的平台。 在UWP中,BitmapImage没有BeginInit或CacheOption.OnLoad。 - Hunter Tran
我在我的应用程序中遇到了同样的情况。@Tuấn Trần,你最终解决了你的问题吗? - Gold.strike
抱歉,我最终使用了一张较小的图片(缩略图)。必须手动创建缩略图。 - Hunter Tran

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