类别:.Net相关知识 / 日期:2013-12-21 / 浏览:2087 / 评论:0
1. 提出问题: 在进行Windows Store App开发的时候,很多的时候会遇到使用ListView绑定大量的数据进行展示,以前在Windows 8的时候,如果不进行处理,加载的时候不仅仅很慢而且UI显示也会被卡死。微软在Windows 8.1中对这样的情况进行了优化,在相同的情况下加载VisualTree的速度快很多,但是在数据未加载完的部分会出现一个默认的占位符,然后用读取出的数据去替代这些占位符。
在默认情况下,Windows 8.1会自动启用占位符,产生这样的效果,如果不需要,我们可以关闭占位符显示,只需要设置ListView的ShowsScrollingPlaceholders属性为False即可。当此属性设置为Flase后,ListView数据的加载就会像网页刷新一样一条一条的显示出来,可以看到滚动条会越来越窄,加载显示的数据会越来越多。但这也不是我们想要的效果,我们需要提升用户的体验效果,在加载的时候要为每一项的占位符显示一个进度条,来提示用户该项还有数据且未加载,这个时候就需要去实现ListView的ContainerContentChanging事件。
2. 前提知识
首先了解下ContainerContentChanging事件。该事件有两个参数一个是ListViewBase类型的sender还有一个是ContainerContentChangingEventArgs类型的args。前者主要是为 ListView 和 GridView 提供基础架构,后者主要是为事件提供参数的,接下来我们需要使用的就是它提供的参数。
ContainerContentChangingEventArgs类:
方法 | |
RegisterUpdateCallback(TypedEventHandler(ListViewBase, ContainerContentChangingEventArgs)) | 注册要在下一阶段再次调用的事件处理程序。 |
RegisterUpdateCallback(UInt32, TypedEventHandler(ListViewBase, ContainerContentChangingEventArgs)) | 注册要在指定阶段再次调用的事件处理程序。 |
属性 | |
Handled:bool | 获取或设置将路由事件标记为已处理的值。 |
InRecycleQueue:bool | 获取一个值,该值指示此容器是否位于 ListViewBase 的回收队列中,且未用于可视化数据项。 |
Item:object | 获取与此容器关联的数据项。 |
ItemContainer:SelectorItem | 获取用于显示当前数据项的 UI 容器。 |
ItemIndex:int | 获取与此容器关联的数据项的 ItemsSource 中的索引。 |
Phase:uint | 获取已调用此容器和数据项对的次数。 |
3.解决方法 前台XMAL界面:
<ListView ShowsScrollingPlaceholders="False" SelectionMode="None" IsItemClickEnabled="False" ItemsSource="{Binding NursingInterventionStandard}"> <ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="150" /> <ColumnDefinition Width="1050" /> </Grid.ColumnDefinitions> <ProgressRing Height="40" Grid.Column="0" Grid.ColumnSpan="2" Name="ImagePlaceholder" /> <TextBlock Name="TxbItemName" Grid.Column="0" Text="{Binding NursingInterventionItem}" VerticalAlignment="Center" HorizontalAlignment="Center" /> <ListView Name="ListTop" Grid.Column="1" ItemsSource="{Binding ItemDetails}" ShowsScrollingPlaceholders="False" SelectionMode="None" IsItemClickEnabled="False"> <ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="140" /> <ColumnDefinition Width="90" /> <ColumnDefinition Width="800" /> </Grid.ColumnDefinitions> <ProgressRing Height="40" Grid.Column="0" Grid.ColumnSpan="4" Name="ImagePlaceholder2" /> <TextBlock Name="TxbDetailName" Grid.Column="0" Text="{Binding DetailItemName}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Name="TxbDetailScore" Grid.Column="1" Text="{Binding DetailItemScore}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <ListView Name="ListDetail" Grid.Column="2" SelectionMode="None" ShowsScrollingPlaceholders="False" ItemsSource="{Binding DeductionItems}" IsItemClickEnabled="False"> <ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="520" /> <ColumnDefinition Width="100" /> <ColumnDefinition Width="120" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding DeductionItemName}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Grid.Column="1" Text="{Binding DeductionItemScore}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <TextBlock Grid.Column="2" Text="{Binding IsRightDeductionItem}" VerticalAlignment="Center" HorizontalAlignment="Center" /> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView>
在上述的界面中,共有3个ListView,这样的多层嵌套的ListView在我们的开发中比较常见,尤其是在使用这样的表格去展示数据的时候。下面我们通过添加前两层ListView的ContainerContentChanging事件,来添加带进度条的占位符。 为第一个ListView添加ContainerContentChanging事件:
private void ListViewFirst_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) { var templateRooot = args.ItemContainer.ContentTemplateRoot as Grid; var projectItem = args.Item as InterventionItem; if (templateRooot == null || projectItem == null) { return; } var imgPlaceholder = templateRooot.FindName("ImagePlaceholder") as ProgressRing; var txtItemName = templateRooot.FindName("TxbItemName") as TextBlock; var listTop = templateRooot.FindName("ListTop") as ListView; if (imgPlaceholder == null || txtItemName == null || listTop == null) { return; } if (args.Phase == 0) { imgPlaceholder.IsActive = true; txtItemName.Opacity = 0; listTop.Opacity = 0; args.RegisterUpdateCallback(ListViewFirst_ContainerContentChanging); } else if (args.Phase == 1) { imgPlaceholder.IsActive = false; txtItemName.Text = projectItem.NursingInterventionItem; txtItemName.Opacity = 1; listTop.ItemsSource = projectItem.ItemDetails; listTop.Opacity = 1; } args.Handled = true; }
为第二个ListView添加ContainerContentChanging事件:
private void ListViewScond_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) { var templateRooot = args.ItemContainer.ContentTemplateRoot as Grid; var projectItem = args.Item as InterventionItemDetail; if (templateRooot == null || projectItem == null) { return; } var imgPlaceholder = templateRooot.FindName("ImagePlaceholder2") as ProgressRing; var txbDetailName = templateRooot.FindName("TxbDetailName") as TextBlock; var txtDetailScore = templateRooot.FindName("TxbDetailScore") as TextBlock; var listDetail = templateRooot.FindName("ListDetail") as ListView; if (imgPlaceholder == null || txbDetailName == null || txtDetailScore == null || listDetail == null) { return; } if (args.Phase == 0) { imgPlaceholder.IsActive = true; txbDetailName.Opacity = 0; txtDetailScore.Opacity = 0; listDetail.Opacity = 0; args.RegisterUpdateCallback(ListViewScond_ContainerContentChanging); } else if (args.Phase == 1) { imgPlaceholder.IsActive = false; imgPlaceholder.IsActive = false; txtItemName.Text = projectItem.NursingInterventionItem; txtItemName.Opacity = 1; listTop.ItemsSource = projectItem.ItemDetails; listTop.ContainerContentChanging += ListView_ContainerContentChanging; listTop.Opacity = 1; } args.Handled = true; }
4、代码注解
要在ListView项中为Item添加一个进度条的元素,并且调整合适大小使其充满这一行。
使用ItemContainer下ContentTemplateRoot获取当前ListView下的DataTemplate模板,用于找到我们要替换的元素。
使用Item获取到要显示的数据项。
当第一次调用事件的时候Phase为0,使用RegisterUpdateCallback方法可以再次调用该事件,并且Phase每调用一次就会加1。
在事件第一次调用的时候只显示进度条,并且将其他元素都设置为隐藏。在第二次调用的时候将隐藏进度条并且显示这一项的真正内容。
使用RegisterUpdateCallback方法的另一种重载可以指定Phase的值。
前台代码只需要在第一个ListView注册ContainerContentChanging事件,在事件当Phase为1的时候为下一个ListView注册事件即可。
发表评论 /