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
/*日志開始*/
本文來源於《深入理解Windows Phone 8.1 UI控件編程》
源代碼下載:http://vdisk.weibo.com/s/zt_pyrfNHoezI
歡迎關注我的微博@WP林政
WP8.1技術交流群:372552293