我正在实现一个可以轻松包含10,000张小图片的列表。实际用例显示的是视频缩略图的列表,因此您可以逐帧滚动视频。我每隔2/3秒将视频的缩略图放入列表中。我需要支持很长的视频(例如1小时视频)。
因此,虚拟化选项:
http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh780657.aspx
我尝试了“增量数据虚拟化”,这对我来说消耗太多内存,因为只能通过流引用图像,最终我会打开10,000个流。由于内存不足,这将导致Windows Phone应用程序崩溃。
现在,我想尝试“随机访问数据虚拟化”。我看到了如何实现IObservableVector<object>, INotifyCollectionChanged
接口(interface)(是的<object>
b/c <T>
不起作用)。棘手的部分是如何处置图像和加载图像。加载图像是一种异步方法。
此外,我认为该解决方案应该具有占位符,就像MSFT文档所说的那样:“在照片查看应用程序中经常看到这种数据虚拟化的示例。该应用程序显示了占位符图像,而不是让用户等待下载相册中的所有照片。在检索每张图像时,应用程序会用实际照片的呈现方式替换该图像的占位符元素。即使尚未下载并显示所有图像,用户仍可以平移图像并与集合进行交互。”
查看占位符的MSFT示例-使用“ContainerContentChanging”似乎是一条重要路径。我在这里猜测,有一种方法可以在此事件中处理图像,并开始加载图像。
https://code.msdn.microsoft.com/windowsapps/ListViewSimple-d5fc27dd
将其归结为一个问题-在哪里可以处置镜像流并开始为随机访问虚拟化列表提供镜像加载? 这是照片应用程序中非常常见的场景,在iOS中非常容易实现,但是似乎没有人在Windows运行时上做到这一点。
最佳答案
您必须调整VirtualizingCollection的实现,请检查以下文章http://www.codeproject.com/Articles/34405/WPF-Data-Virtualization。
我使用适用于Windows Phone 8.1 Runtime App的VirtualizingCollection编写了一个示例应用程序。
public class ThumbnailItem
{
public Uri ImageUri { get; set; }
}
稍后编写ThumbnailItem提供程序。
public class ThumbnailProvider : IItemsProvider<ThumbnailItem>
{
private readonly int _itemsCount;
public ThumbnailProvider(int itemsCount)
{
_itemsCount = itemsCount;
}
public int FetchCount()
{
return _itemsCount;
}
public IList<ThumbnailItem> FetchRange(int startIndex, int count)
{
var items = new List<ThumbnailItem>();
while (count-- > 0)
{
items.Add(new ThumbnailItem()
{
ImageUri = new Uri("ms-appx:///Assets/Square71x71Logo.scale-240.png")
});
}
return items;
}
}
然后,您必须在ViewModel内部创建一个IList属性,并使用VirtualizingCollection的实现来设置值。我建议您使用AsyncVirtualizingCollection。
Items = new AsyncVirtualizingCollection<ThumbnailItem>(new ThumbnailProvider(1000000), 100);
最后,在 View 上,必须使用ViewModel的实例设置DataContext对象,并且ListView应该类似于:
<ListView
ItemsSource="{Binding Items,Mode=OneWay}"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="0 0 20 20">
<Image Source="{Binding ImageUri,Mode=OneTime}"
Width="72" Height="72"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
当然,必须根据您的要求更改提供者的逻辑,我编写的代码仅是示例。
请帮助您将其标记为答案。
此致,
丹尼斯
编辑
friend @Quincy,我发布了一个简单的示例,您可以对其进行调整。也许对于您的应用程序,ThumbnailItem类将包含指示IsolateStorageFile文件名的Filename属性。在这种情况下,您必须创建一个带有转换器的绑定(bind),因此您需要实现一个IValueConverter对象,以使用IsolateStorageFile创建一个BitmapImage实例。
关于图像关闭,VirtualizingCollection已定义了一个页面大小,默认情况下为100。您的IsolateStorageFiles将被使用一次,以在IValueConverter对象中创建BitmapImage。以后,如果未使用旧页面,则VirtualizingCollection将删除旧页面(未显示,请检查VirtualizingCollection实现),最后,GC将关闭并处置BitmapImage。
移植VirtualizingCollection很容易,我记得我刚刚对AsyncVirtualizingCollection类进行了更改。我的解决方案很简单:
将ThreadPool.QueueUserWorkItem替换为ThreadPool.RunAsync。
替换调试跟踪(只是调试消息,不是很重要)。
使用以下方法替换SynchronizationContext方法调用:
(适用于Windows Phone应用)CoreApplication.MainView.CoreWindow.Dispatcher。
(对于Windows应用程序)CoreApplication.MainView.Dispatcher。
希望对您有帮助。