如何快速加载和显示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
中,在加载下一个图像之前,图像总是空白的,这是一个令人不愉快的效果。
如果你不能使用准备好的预览(缩小)图像,至少不要渲染图像的完整尺寸。为了避免这样做,请使用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;
});
}
注意,这只是一个示例代码,需要做一些工作。例如,如果您在预览生成的过程中更改了选定的文件(因为它们是异步的)-您需要取消所有挂起的操作,以避免用以前的操作覆盖当前的文件预览。但这应该很简单。