曾将找到过关于 ListBox滚动加载数据的文章,但是现在去查找的时候,发现找不到那些文章了。不过,意外的找到一篇更好的有关ListBox滚动加载数据的帖子http://social.msdn.microsoft.com/Forums/zh-CN/windowsphonezhchs/thread/4b9ebac8-e1c1-4522-9f11-5e0567f95182 中找到的 http://www.bbniu.com/thread-1569-1-1.html,为了避免下次找不到该文章和方便查看内容,下面附上该篇文章所有的内容:
本示例的主要目的是实现ListBox滚动加载数据。假设,默认为30条数据,当滚动到底部的时候便再次加载20条数据,以此类推。
需要解决的问题:如何判断滚动到底部,以及处理加载数据的请求?
思路如下:
1.检测ListBox中的ScrollViewer控件状态。
2.若状态不为滚动中:根据ScrollViewer的ExtentHeight与VerticalOffset,判断是否到底,并执行请求加载数据。
一、添加视图状态检测代码
首先,在XAML中添加一个空的ListBox,如下:
XAML添加完成后,在后台代码中实现检测ListBox的ScrollViewer状态改变的代码,在这个步骤中需要另外2个方法 :
1.根据视图树查找指定对象视图树中的元素 代码如下:
2.根据给定元素和状态名,查找到元素中的视图状态。代码如下:
具有这2个方法后,再来创建一个向ListBox添加数据的方法。代码如下:
那么,做完准备工作了。下面注册页面Load事件,并在事件中使用以上方法,来实现我们的目的。代码如下:
二、如何判断ListBox滚动的位置
在代码中已经实现了关键步骤,即捕捉到ListBox包含的ScrollViewer视图状态更改事件。那么下面我们需要解决的问题就是如何判断是否滚动到底部,以及判断是否处于滚动中,来添加数据了。解决这个问题需要知道ScrollViewer的几个属性:
1.ExtentHeight 获取 ScrollViewer 中显示的所有内容的垂直大小。(总数)
2.VerticalOffset 获取一个值,该值包含滚动内容的垂直偏移量。 (滚动到第几个)
3.ViewportHeight 获取一个值,该值包含可见内容的垂直大小。 (页面能显示几个)
知道这几个值的意思,那么就可以计算出当前滚动的位置了:
默认为ListBox添加30条数据,执行后运行的效果,通过几个运行效果来看看:
初始效果:
下面是滚动过程中:
可以看到,ExtentHeight=30,当前VerticalOffset=10.5。并没有滚动到底部。
继续滚动。
此时已经滚动到底部,添加Item代码已经执行,实际上此时的为ExtentHeight=50。
三、结尾
ListBox动态加载数据已经完成了。那么这个实现有什么问题呢?
1.假如数据来自服务器,存在异步延迟,并且耗时的操作会影响UI线程?
2.避免 在异步加载过程中多次滚动到底的请求。
参考连接
----------------------------------------------------------------
1.Windows Phone Mango change, Listbox: How to detect compression(end of scroll) states ?
2.How to detect when a list is scrolling (or not)
a.之前找到的是关于listBox滚动到底加载数据,用的是下面这个方法(后面的注释是我猜测原来作者的意思):
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);//寻找obj的视图树下的子元素
if (child != null && child is childItem)//寻找给定的子元素类型childItem,符合则返回。
return (childItem)child;
else//不符合则重复函数FindVisualChild<childItem>(child);来查找,直到找到匹配函数中给定的子元素类型
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
}
存在的问题:1)该函数一开始就寻找obj的子元素,然后再用这个子元素去寻找匹配的子元素类型,对于像scrollViewer这类控件寻找scrollviewer,就始终会使结果处于null;2)而且一旦if…else…,childItem childOfChild = FindVisualChild<childItem>(child);循环一次继续查找,如果再找不到,那么得到的最终值就是null。for循环写在这里没什么用。
b.这篇文章就有一个很好的方法:
private childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
while (obj != null)
{
if (obj is childItem)
return obj as childItem;
obj = VisualTreeHelper.GetChild(obj, 0);
}
return null;
}
很好地解决了a产生的问题。1)直接开始寻找匹配的子元素类型,就不存在有些控件的直接子元素就是要找的子元素类型。如果第一层子元素找不到,那么VisualTreeHelper.GetChild(obj, 0);获取下一层的子元素,然后继续查找匹配的子元素类型;2)nullwhile (obj != null)表明该函数基本上就不会返回null,会一直匹配查找,直到查找到。
关于是listBox拉到底还是拉动一部分就产生操作,就需要根据需求不同来决定了。根据listBoxVisual的滚动状态StateGroup group = MainBean.FindVisualState(element, "ScrollStates");和group.CurrentStateChanged 事件来判断。scrollViewer.VerticalOffset获取现在滚动条拉动的高度。scrollViewer.VerticalOffset >= scrollViewer.ScrollableHeight(滚动到底,滚动条>实际可滚动高度);