I'm trying to write a little photo viewer in WPF, basically emulating what Windows Photo Viewer is offering.
Displaying in both windowed and fullscreen mode is done using an Image
<Image Name="ImgDisplay" Source="{Binding CurrentImage.FullPath, Converter={StaticResource FilenameToImageConverter}}"/>
where the FilenameToImageConverter
does the following
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();
}
}
When testing the program with my photos however (around 8mpx, 4MB jpeg files), the load times for displaying the images are huge (2 or 3 seconds), while Windows Photo Viewer is able to skip through the images with ease. I see it first displaying a lower-resolution version of the image, shortly before displaying the full one. Everything however ends up way faster than my approach.
How can I achieve this? Is it all through thumbnails/preloading?
Thank you in advance
Edit
Thanks, the tips given, scaling down using DecodePixelWidth
as well as Async/OneWay-Bindings have improved the situation substantially, though not enough to make everything fluent. Also with IsAsync=true
, the image will always be blank before loading the next image, which is an unpleasant effect.
It would be nice to solve that through displaying a highly downscaled version immediately and afterwards display the full image when it has been loaded asynchronously. As there is some sort of temporal succession involved, I have no idea how to implement that with bindings. Any suggestions, please?
Best Answer
If you cannot use prepared preview (downscaled) images, at least do not render image at it's full size. To avoid doing that, use
DecodePixelWidth
(orDecodePixelHeight
) property. Set it to some reasonable value (maybe based on current monitor resolution), and you will already see significant perfomance improvement:EDIT in response to comments. With just converter it's not easy to achieve what you want, but you can just add one more property to your ViewModel and do it like this (note that you now need to bind to CurrentImage directly, without converters):
Note that's just a sample code and needs a bit of work. For example, if you change selected file in the middle of preview generation (since they are asynchronous) - you need to cancel all pending operations to not overwrite current file preview with previous one. But that should be easy enough.