这是我之前提问how should a metro app cache images for tombstoning (and should it)?
的扩展我发现的解决方案是使用HttpClient,但这会导致Web服务器上每个文件不必要的第二次命中。
是否有保存Image / ImageSource的现有图像/流(使用ImageOpened事件以确保它可用)?
解决方案必须适用于当前的RP API,因为这是一个从CP进行大量更改的区域。
答案 0 :(得分:2)
首先请注意,没有做任何自定义(即使用URI作为Image.Source),已经实现了某种缓存,就好像你的应用程序旁边有一个Fiddler,你和#39; ll看到请求发出一次,而不是每次都显示一个项目。
这就是说,如果你想要某种持久性缓存,你可以创建一个转换器来检查所需的图像是否在App的临时目录中,如果不是这样的话就下载它。
这是一种方法:
public sealed class UriToCachedImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
Uri uri = value as Uri;
if (uri == null)
return null;
// Don't let user know those are images, yet keep a unique and consistent name.
string fileName = Path.GetFileName(uri.AbsolutePath);
StorageFile file = null;
try
{
Task<StorageFile> task = ApplicationData.Current.TemporaryFolder.GetFileAsync(fileName).AsTask<StorageFile>();
task.Wait();
file = task.Result;
}
catch (AggregateException ex)
{
if (ex.InnerException is FileNotFoundException)
{
// http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/1eb71a80-c59c-4146-aeb6-fefd69f4b4bb/
}
else
{
throw;
}
}
if (file == null)
{
// File isn't present in cache => download it.
// This creates a file in the app's INetCache
// but we can't retrieve its path or know its name for sure
// hence we're not using this file as our cache file.
Task<StorageFile> streamTask = StorageFile.CreateStreamedFileFromUriAsync(fileName, uri, RandomAccessStreamReference.CreateFromUri(uri)).AsTask<StorageFile>();
streamTask.Wait();
StorageFile storageFile = streamTask.Result;
// Copy file to our temporary directory (can't move as we don't have write access on the initial file).
Task<StorageFile> copyTask = storageFile.CopyAsync(ApplicationData.Current.TemporaryFolder, fileName, NameCollisionOption.ReplaceExisting).AsTask<StorageFile>();
copyTask.Wait();
file = copyTask.Result;
}
Task<IRandomAccessStreamWithContentType> openTask = file.OpenReadAsync().AsTask<IRandomAccessStreamWithContentType>();
openTask.Wait();
IRandomAccessStreamWithContentType randomAccessStream = openTask.Result;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(randomAccessStream);
return bitmapImage;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
注意:此代码段还说明了如何从同步方法调用异步方法;)
答案 1 :(得分:0)
我看不到从ImageOpened事件中做出我需要的方法,而是找到了一个工作模型。
而不是将图像uri绑定到Image Source并让metro下载图像,而是绑定到Image的Tag,然后在数据绑定完成后循环遍历xaml中的每个Image并将图像下载为RandomAccessStream,保存流并将Image Source设置为从流创建的BitmapImage。
RandomAccessStreamReference rasf = RandomAccessStreamReference.CreateFromUri(new Uri(MyImage.Tag as string));
BitmapImage bitmapImage = new BitmapImage();
var ras = await rasf.OpenReadAsync();
bitmapImage.SetSource(ras);
MyImage.Source = bitmapImage;
我还通过将代码放在转换器和视图模型属性中来调查自动化过程,但由于异步调用,这些都不可能。
答案 2 :(得分:0)
你也可以使用FFImageLoading(https://github.com/molinch/FFImageLoading/)或查看其来源以了解它是如何实现的(https://github.com/molinch/FFImageLoading/tree/master/FFImageLoading.Windows)
这很简单:
<ff:FFImage Name="image"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
LoadingPlaceholder="loading.png"
ErrorPlaceholder="error.png"
CacheDuration="30"
RetryCount="3"
RetryDelay="250"
DownsampleHeight="300"
Source="http://lorempixel.com/output/city-q-c-600-600-5.jpg">
</ff:FFImage>
示例项目:https://github.com/molinch/FFImageLoading/tree/master/samples/Simple.WinPhone.Sample