类别:.Net相关知识 / 日期:2013-12-21 / 浏览:2380 / 评论: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注册事件即可。



发表评论 /