我试图在WPF中编写一个小照片查看器,基本上模仿 Windows Photo Viewer 提供的内容。
使用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 Photo Viewer可以轻松跳过图像。我看到它首先显示图像的低分辨率版本,在显示完整图像之前不久。然而,一切都比我的方法更快。
我怎样才能做到这一点?是通过缩略图/预加载吗? 提前谢谢
谢谢,给出的提示,使用DecodePixelWidth
缩减以及Async / OneWay-Bindings已经大大改善了这种情况,但还不足以让一切都流畅。同样使用IsAsync=true
,在加载下一张图像之前,图像将始终为空白,这是一种令人不快的效果。
通过立即显示高度缩小的版本 来解决这个问题会很好,然后在异步加载时显示完整的图像。由于涉及某种时间继承,我不知道如何使用绑定实现它。有什么建议吗?
答案 0 :(得分:2)
如果你不能使用准备好的预览(缩小尺寸)图像,至少不要渲染图像的全尺寸。为避免这样做,请使用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;
});
}
请注意,这只是一个示例代码,需要一些工作。例如,如果您在预览生成过程中更改了所选文件(因为它们是异步的) - 您需要取消所有待处理操作,以便不使用上一个操作覆盖当前文件预览。但这应该很容易。