11.2.2 VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件
VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid都是虚拟化布局控件,一般情况下在界面的布局上很少会用到这些虚拟化排列的控件,大部分都是封装在列表的布局面板上使用,主要的目的就是为了实现列表上大数据量的虚拟化,从而极大地提高列表的效率。那么其实这3个虚拟化布局控件都是列表控件的默认布局排列的方式,其中VirtualizingStackPanel控件是ListBox的默认布局面板,ItemsStackPanel是ListView的默认布局面板,ItemsWrapGrid是GridView的默认布局面板。
VirtualizingStackPanel控件和ItemsStackPanel控件都是表示沿着水平方向或垂直垂直方向将内容虚拟化地排列在一行上。它们控件所实现的排列布局效果和StackPanel控件是一样的,不同的是这些控件可以实现虚拟化的逻辑。对于数据较多的列表布局,使用VirtualizingStackPanel控件或者ItemsStackPanel控件会比StackPanel控件高效很多,因为虚拟化控件只是把当前屏幕范围内的数据显示出来,其他的数据都通过虚拟化的技术进行处理,并没有进行UI的初始化显示,所以效率很高。ItemsWrapGrid控件实现的则是网格的虚拟化布局效果,虚拟化原理也是和ItemsStackPanel控件类似的,只不过他们排列的方式不一样。
这些虚拟化排列布局控件会计算可见项的数量,并处理来自 ItemsControl(如 ListBox)的ItemContainerGenerator,以便只为可见项创建 UI 元素。仅当StackPanel中包含的项控件创建自己的项容器时,才会在该面板中发生虚拟化。 可以使用数据绑定来确保发生这一过程,如果是直接创建列表的项元素然后添加为虚拟化排列布局控件的子对象,那么这种方式是不会进行虚拟化处理的。下面以ItemsStackPanel来说明我们如何去利用虚拟化排列布局控件去解决一些实际的问题。
ItemsStackPanel是ListView元素的默认项宿主。使用ListView列表控件绑定数据的时候,默认是采用了ItemsStackPanel控件对数据项进行排列。如果你使用ItemsControl列表控件来展示数据,要给这个列表增加虚拟化的功能,ItemsStackPanel对象元素必须包含在一个ItemsPanelTemplate中。现在我们再回过头来看本章的第一个例子,用ItemsControl控件绑定到2000个数据项的集合的时候,加载的速度很慢,这就是没有使用虚拟化的结果。如果在ItemsControl控件上使用ItemsStackPanel来进行虚拟化布局,那么你会发现加载的速度非常快。给ItemsControl控件添加ItemsStackPanel虚拟化布局,需要把代码修改成如下:
<ItemsControl x:Name="itemsControl"> <!--使用ItemsStackPanel控件作为ItemsControl的布局面板--> < ItemsControl.ItemsPanel> <ItemsPanelTemplate> <ItemsStackPanel/> </ItemsPanelTemplate> </ ItemsControl.ItemsPanel> <ItemsControl.Template> <ControlTemplate TargetType="ItemsControl"> <ScrollViewer> <ItemsPresenter/> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemTemplate> <DataTemplate> ……省略若干代码 </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
如果在列表中需要使用到ItemsStackPanel控件虚拟化的技术的时候,还要注意一个事情是,不要破坏ScrollViewer和ItemsPresenter的结构,否则将实现不了虚拟化的效果。如果你在ScrollViewer和ItemsPresenter之间再添加一个StackPanel控件,如下所示:
<ControlTemplate TargetType="ItemsControl"> <ScrollViewer> <StackPanel> <ItemsPresenter/> </StackPanel> </ScrollViewer> </ControlTemplate>
那么这时候,ItemsStackPanel控件会一次性把所有的数据都初始化,不会起到虚拟化的作用。因为ItemsStackPanel控件虚拟化的时候是根据每个Item的固定的大小来进行布局的虚拟化处理的,当在ScrollViewer和ItemsPresenter中加入了其他的控件之后会破坏了ItemsStackPanel控件的虚拟化布局,导致ItemsStackPanel控件无法准确地测量出来列表的数据项的布局。
11.2.3 实现横向虚拟化布局
通常我们实现的列表布局大部分都是竖向的布局,包括GridView控件的布局整体上也是竖向的布局。那么ListView控件和ListBox控件默认都是竖向垂直滚动的列表,如果要让其水平滚动那么就需要自定义其布局的面板,这时候我们就可以使用ItemsStackPanel控件去实现了,如果我们并不需要ListView控件的那么多的功能和效果,就可以直接使用最基本的列表控件ItemsControl控件搭配ItemsStackPanel控件去实现横向滚动的效果,并且带有虚拟化的功能。
下面我们用一个例子使用ItemsControl控件横向滚动展示图片,在这个例子里面会使用到ItemsStackPanel控件的Horizontal布局。列表中会有100个数据项,我们通过日志来查看其加载的数据项是怎样的。
代码清单11-7:横向虚拟化列表(源代码:第11章\Examples_11_7)
(1)首先,创建实体类和自定义的集合类,实体类Item和自定义的集合类ItemList。
Item.cs文件主要代码 ------------------------------------------------------------------------------------------------------------------ public class Item { // 图片对象 public BitmapImage Image { get; set; } // 图片名字 public string ImageName { get; set; } }
ItemList.cs文件主要代码 ------------------------------------------------------------------------------------------------------------------ public class ItemList : IList { // 设置集合的数量为100 public ItemList() { Count = 100; } // 集合数量属性 public int Count { get; set; } // 根据索引返回数据项 public object this[int index] { get { // 加载的图片是程序里面的图片资源,5张图片循环加载 int imageIndex = 5 - index % 5; Debug.WriteLine("加载的集合索引是:" + index ); return new Item { ImageName = "图片" + index, Image = new BitmapImage(new Uri("ms-appx:///Images/" + imageIndex + ".jpg", UriKind.RelativeOrAbsolute)) }; } set { throw new NotImplementedException(); } } //……省略若干代码 }
(2)实现ItemsControl的横向虚拟化布局。
要实现ItemsControl的横向虚拟化布局,除了使用ItemsStackPanel控件的Horizontal布局,还需要在ItemsControl中设置ScrollViewer.HorizontalScrollBarVisibility="Auto",这样列表就可以水平滚动了。列表的代码如下:
MainPage.xaml文件主要代码 ------------------------------------------------------------------------------------------------------------------ <ItemsControl x:Name="list"> <!--使用ItemsStackPanel控件作为ItemsControl的布局面板--> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <ItemsStackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.Template> <ControlTemplate TargetType="ItemsControl"> <ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility ="Disabled"> <ItemsPresenter/> </ScrollViewer> </ControlTemplate> </ItemsControl.Template> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel> <Image Source="{Binding Image}" Width="144" Height="240" Stretch="UniformToFill"></Image> <TextBlock Text="{Binding ImageName}"></TextBlock> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
MainPage.xaml.cs文件主要代码 ------------------------------------------------------------------------------------------------------------------ public MainPage() { InitializeComponent(); list.ItemsSource = new ItemList(); }
(3)列表的运行效果如图11.8所示,采用Debug调试下运行程序可以看到日志显示列表只是初始化了10个数据项,日志如下所示:
/*日志开始*/
加载的集合索引是:0
加载的集合索引是:1
加载的集合索引是:2
加载的集合索引是:3
加载的集合索引是:4
加载的集合索引是:5
加载的集合索引是:6
加载的集合索引是:7
加载的集合索引是:8
加载的集合索引是:9
/*日志开始*/
源代码下载:http://vdisk.weibo.com/s/zt_pyrfNHoezI
[WP8.1UI控件编程]Windows Phone VirtualizingStackPanel、ItemsStackPanel和ItemsWrapGrid虚拟化排列布局控件