今天和大家分享一些关于windows phone ui虚拟化和数据虚拟化的一些知识。
也顺便回答我上一篇【LongListSelector 控件 在 wp7 和wp8中的不同之处】里,留下的那个问题,微软为什么推荐使用longlistselector.
MainPageViewMode.cs
public class MainPageViewModel
{
//省略
public MainPageViewModel()
{ //我们模拟1000条商品数据
listProduct = new List<Product>(1000);
for (int i = 0; i < 1000; i++)
{
listProduct.Add(new Product { Id = i, Name = "产品-" + rnd.Next(1000, 10000).ToString(), Category = "" });
} } }
Mainpage.xaml
<DataTemplate x:Key="LBDataTemplate">
<Grid Loaded="Grid_Loaded" d:DesignWidth="411.045" d:DesignHeight="78.209">
<TextBlock HorizontalAlignment="Left" Margin="10,17,0,25" TextWrapping="Wrap" Foreground="Black"
Text="{Binding Name}" Height="36" Width="197" FontSize="30"/>
</Grid>
</DataTemplate>
}" />
Loaded="Grid_Loaded" 此事件触发说明模板被加载。这里我们用的是ListBox控件
MainPageViewModel vm;
public MainPage()
{
InitializeComponent();
vm= new MainPageViewModel();
LayoutRoot.DataContext = vm;
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
vm.LoadItemCounter++;//界面上item被渲染的数目
}
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<StackPanel/>
</ItemsPanelTemplate>
为什么会产生这种情况?
因为实现listbox的关键是默认的容器 VirtualizingStackPanel 通过工具编辑变成了StackPanel
改回VirtualizingStackPanel
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<VirtualizingStackPanel/>
>
下面我们来看一下LonglistSelector
书接上回的Windows phone UI虚拟化和数据虚拟化(一)我们学习了wp的ui虚拟化。
今天来和大家分享一下wp的数据虚拟化。
并同时感谢我的同事dgwutao在编写此文时给我的巨大帮助,3ks!
--弱水三千,只取一瓢饮。百万记录,只载十几条。
【.xaml】
<phone:LongListSelector ItemsSource="{Binding ListProduct}"
【.cs】
public class VirtualizingCollection<T>:IList<T>,IList, INotifyPropertyChanged, INotifyCollectionChanged
{
public VirtualizingCollection(int count)
{
this.count = count;
}
public int Count
{
get { return count; }
set
{
count = value;
RaisePropertyChanged("Count");
}
}
public T this[int index]
{
get
{
Return null;
}
set
{
throw new NotImplementedException();
}
}
//----省略-----
}
So Easy! 我们只要在VirtualizingCollection初始化的时候给Count付个1000,就轻松的骗过那些傻傻的列表控件,可以尽情的滚动了。
尽管能刷刷的滚动了,但是我们发现,界面上一片空白,没有任何数据。为啥呢?
好,现在我们来假设这样一个qq聊天的场景。
在进入qq会话页面的时候会从本地数据库里读取聊天的历史记录。共有1000条记录。每页显示9条。
这里我们可以按照传统的分页方式来加载。
在上面的类里,我们新建一个字典字段。
private readonly Dictionary<int, IList<T>> _pages = new Dictionary<int, IList<T>>();
下面是具体的代码
private readonly int _pageSize = 9;
public T this[int index]
{
get
{
int pageIndex = index / PageSize;//请求的item的索引(index)所在的页码
int pageOffset = index % PageSize;//滑动列表当前页产生的偏移
RequestPage(pageIndex);
//大于一屏的一半加载下一页
if (pageOffset > PageSize / 2 && pageIndex < Count / PageSize)
{
RequestPage(pageIndex + 1);
}
// 小于一屏的一半加载上一页
if (pageOffset < PageSize / 2 && pageIndex > 0)
{
RequestPage(pageIndex - 1);
}
//只保留,当前页和上下两页,删除多余的
if (_pages.Count >= 3 && _pages.ContainsKey(pageIndex - 2))
{
_pages.Remove(pageIndex - 2);
}
if (_pages.Count > 3 && _pages.ContainsKey(pageIndex + 2))
{
_pages.Remove(pageIndex + 2);
}
return _pages[pageIndex][pageOffset];
}
}
protected virtual void RequestPage(int pageIndex)
{
if (!_pages.ContainsKey(pageIndex))
{
_pages.Add(pageIndex, null);
LoadPage(pageIndex);
}
}
protected virtual void LoadPage(int pageIndex)
{
PopulatePage(pageIndex, FetchPage(pageIndex));
}
protected virtual void PopulatePage(int pageIndex, IList<T> page)
{
if (_pages.ContainsKey(pageIndex))
_pages[pageIndex] = page;
}
protected IList<T> FetchPage(int pageIndex)
{ //这里是从数据库里取,列表请求的页码对应的数据
return (IList<T>)LocalData.FetchRange(pageIndex * PageSize, PageSize);
}
public static class LocalData
{
private static Random rnd = new Random();
private static List<Product> listProduct;
static LocalData()
{ //假设这里是存在本地数据库里的聊天记录的信息有1000条
listProduct = new List<Product>(1000);
for (int i = 0; i < 1000; i++)
{
listProduct.Add(new Product { Id = i, Name = "聊天记录-" + rnd.Next(1000, 10000).ToString(), Category = "" });
}
}
//分页取库里的聊天记录(模拟)
public static IList<Product> FetchRange(int skip, int take)
{
return listProduct.Skip(skip).Take(take).ToList();
}
}
private VirtualizingCollection<Product> vList;
public VirtualizingCollection<Product> VList
{
get { return vList; }
set { vList = value; }
}
private static Random rnd=new Random ();
public MainPageViewModel()
{
vList = new VirtualizingCollection<Product>(1000);
}
我们看到内存中加载的页数始终是3-5页。而且滚动条已经显示的是总数为1000时的高度。
这就是所谓的数据虚拟化了。
最后我还要留个小小的问题。public class VirtualizingCollection<T>:IList<T>,IList在这里我实现了两个很相近的接口。
谁能告诉我这么做是为啥?请留言回答:) 回答正确的,月薪15k以下的,你们老板对不起你!