如何快速加载和显示WPF/BitmapImage图像

本文关键字:BitmapImage 图像 WPF 显示 何快速 加载 | 更新日期: 2023-09-27 18:06:56

我正在尝试在WPF中编写一个小的照片查看器,基本上是模仿Windows照片查看器提供的功能。

在窗口和全屏模式下显示使用Image

<Image Name="ImgDisplay" Source="{Binding CurrentImage.FullPath, Converter={StaticResource FilenameToImageConverter}}"/>

,其中FilenameToImageConverter执行以下操作

public class FilenameToImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string uri = value as string;
            if (uri != null && File.Exists(uri))
            {
                BitmapImage image = new BitmapImage();
                image.BeginInit();
                image.CacheOption = BitmapCacheOption.None;
                image.UriCachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.BypassCache);
                image.CacheOption = BitmapCacheOption.OnLoad;
                image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
                image.UriSource = new Uri(uri);
                image.EndInit();
                return image;
            }
            return null;
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

当用我的照片(大约8mpx, 4MB jpeg文件)测试程序时,显示图像的加载时间非常长(2或3秒),而Windows照片查看器可以轻松跳过图像。我看到它首先显示图像的低分辨率版本,然后才显示完整的图像。然而,每件事最终都比我的方法快得多。

我怎样才能做到这一点?都是通过缩略图/预加载吗?提前谢谢你

编辑

谢谢,给出的提示,使用DecodePixelWidth以及异步/单向绑定的缩减已经大大改善了这种情况,尽管还不足以使一切流畅。同样在IsAsync=true中,在加载下一个图像之前,图像总是空白的,这是一个令人不愉快的效果。

这将是很好的解决方案,通过显示一个高度缩小的版本立即,然后显示完整的图像时,它已被异步加载。由于涉及到某种时间继承,我不知道如何用绑定实现它。有什么建议吗?

如何快速加载和显示WPF/BitmapImage图像

如果你不能使用准备好的预览(缩小)图像,至少不要渲染图像的完整尺寸。为了避免这样做,请使用DecodePixelWidth(或DecodePixelHeight)属性。将其设置为合理的值(可能基于当前的监视器分辨率),您将已经看到显著的性能改进:

public class FilenameToImageConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        string uri = value as string;
        if (uri != null && File.Exists(uri)) {
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = new Uri(uri);
            image.DecodePixelWidth = 1920; // should be enough, but you can experiment
            image.EndInit();
            return image;
        }
        return null;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        throw new NotSupportedException();
    }
}

编辑对评论的回应。仅使用转换器,它不容易实现你想要的,但你可以只添加一个属性到你的ViewModel,并这样做(注意,你现在需要绑定到CurrentImage直接,没有转换器):

    private string _currentFile;
    public string CurrentFile
    {
        get { return _currentFile; }
        set
        {
            if (value == _currentFile) return;
            _currentFile = value;
            OnPropertyChanged();
            UpdateImage();
        }
    }
    private ImageSource _currentImage;
    public ImageSource CurrentImage
    {
        get { return _currentImage; }
        set
        {
            if (Equals(value, _currentImage)) return;
            _currentImage = value;
            OnPropertyChanged();
        }
    }
    private async void UpdateImage() {
        var file = this.CurrentFile;
        // this is asynchronous and won't block UI
        // first generate rough preview
        this.CurrentImage = await Generate(file, 320);
        // then generate quality preview
        this.CurrentImage = await Generate(file, 1920);            
    }
    private Task<BitmapImage> Generate(string file, int scale) {
        return Task.Run(() =>
        {
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = new Uri(file);
            image.DecodePixelWidth = scale;
            image.EndInit();
            image.Freeze(); // important
            return image;
        });
    }

注意,这只是一个示例代码,需要做一些工作。例如,如果您在预览生成的过程中更改了选定的文件(因为它们是异步的)-您需要取消所有挂起的操作,以避免用以前的操作覆盖当前的文件预览。但这应该很简单。