我正在尝试在 WPF 中编写一个小照片查看器,基本上模拟Windows 照片查看器提供的功能。
以窗口和全屏模式显示是使用Image
<Image Name="ImgDisplay" Source="{Binding CurrentImage.FullPath, Converter={StaticResource FilenameToImageConverter}}"/>
Run Code Online (Sandbox Code Playgroud)
其中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();
}
}
Run Code Online (Sandbox Code Playgroud)
然而,当用我的照片(大约 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();
}
}
Run Code Online (Sandbox Code Playgroud)
编辑回应评论。仅使用转换器并不容易实现您想要的效果,但是您可以向 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;
});
}
Run Code Online (Sandbox Code Playgroud)
请注意,这只是示例代码,需要一些工作。例如,如果您在预览生成过程中更改选定的文件(因为它们是异步的) - 您需要取消所有挂起的操作,以免用前一个文件预览覆盖当前文件预览。但这应该很容易。