APP开发,列表的分页加载是再常见不过的需求,我以前对每个需要分页加载的列表控件都写了一套分页加载逻辑,后来看官方的Demo,才发现其实这些相似的代码是可以封装起来的。基于WinRT开发APP的人应该知道,在WinRT下有ISupportIncrementalLoading这么一个接口,这个接口一个很重要的作用就是可以实现列表控件的分页加载。比如ListView,当ListView的ItemSource绑定的数据源实现了ISupportIncrementalLoading接口,那么当滑动到ListView底部的时候,会自动调用ISupportIncrementalLoading的方法实现自动加载。
可是在基于Silverlight的WP上是没有ISupportIncrementalLoading这个接口的,那下面就在Wp7上来实现一个简易版的列表分页加载接口。
首先我们定义这个接口,这个接口只有一个方法:LoadMoreAsync,这个方法的作用就是根据参数加载数据,你可以把参数理解成PageSize。
public interface ISupportIncrementalLoading
{
Task<LoadMoreItemsResult> LoadMoreAsync(uint count);
}
当我们加载一页数据,会有一些通用的逻辑,比如说判断是否还有下一页;比如说,在加载前后我们可能还要进行一些操作,这些都可以封装起来,所以我们定义一个抽象类来实现这个逻辑,此外,当我们加载数据往往是多条,并且要实现集合绑定,以便在界面显示新加载数据,这里为了简便我们直接让抽象类继承ObservableCollection<T>。抽象类定义如下:
public abstract class IncrementalLoadingDataSource<T> : ObservableCollection<T>, ISupportIncrementalLoading { public async Task<LoadMoreItemsResult> LoadMoreAsync(uint count) { if (CanLoadMore()) { ICollection<T> list = await LoadMore(count); foreach (var item in list) { this.Add(item); } return new LoadMoreItemsResult() { LoadedCount = list.Count }; } else { return new LoadMoreItemsResult() { LoadedCount = 0 };; } } protected abstract Task<ICollection<T>> LoadMore(uint count); protected abstract bool CanLoadMore(); }
抽象类的实现逻辑比较简单,主要是判断是否还有下一页,如果有就请求下一页数据。这里有两个抽象方法我们没有实现,因为真正加载数据以及判断是否还有下一页可能都是跟业务相关,需要各自具体实现的,因此这里没有具体实现。这里给出一个实现样例,比如我们要实现StudentInfo的分页加载,可以如下定义:
public class StudentInfo { public string Name { get; set; } } public class Students : IncrementalLoadingDataSource<StudentInfo> { protected async override Task<ICollection<StudentInfo>> LoadMore(uint count) { return await Task.Factory.StartNew<ICollection<StudentInfo>>(() => { List<StudentInfo> list = new List<StudentInfo>(); for (int i = 0; i < count; i++) { list.Add(new StudentInfo() { Name = i.ToString() }); } return list; }); } protected override bool CanLoadMore() { return true; } }
现在接口都已经定义好了,我们的目的是让所有继承自IncrementalLoadingDataSource的实体数据都可以分页加载。
我们该如何把接口ISupportIncrementalLoading与我们的列表控件关联起来?这里我们借助附加属性来实现,我们定义一个类叫IncrementalLoadingHelper,类中定义一个附加属性叫IncrementalLoading,只有当列表控件的IncrementalLoading为true时,并且列表控件的ItemsSource的数据源实现ISupportIncrementalLoading就可以分页加载。IncrementalLoadingHelper定义如下,其主要逻辑是在设置IncrementalLoading时,获取到列表控件,进而获取到列表控件的ScrollBar,并绑定ScrollBar的ValueChanged事件,在ValueChanged事件中,判断是否到底部,如果到底部,则调用列表控件的ItemsSource的LoadMoreAsync方法,前面介绍过,ItemsSource必须实现ISupportIncrementalLoading接口。
public class IncrementalLoadingHelper { public const uint PAGE_SIZE = 25; public static DependencyProperty IncrementalLoadingProperty = DependencyProperty.RegisterAttached("IncrementalLoading", typeof(Boolean), typeof(IncrementalLoadingHelper), new PropertyMetadata(false, IncrementalLoadingPropertyChangedCallback)); private static ItemsControl _itemsControl; public static void SetIncrementalLoading(DependencyObject obj, bool val) { obj.SetValue(IncrementalLoadingProperty, val); } public static bool GetIncrementalLoading(DependencyObject obj) { return (bool)obj.GetValue(IncrementalLoadingProperty); } private static void IncrementalLoadingPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { ItemsControl control = d as ItemsControl; ScrollBar scrollBar = GetChild<ScrollBar>(d); if (control != null && scrollBar != null && control.Items.Count > 0) { scrollBar.Tag = control; _itemsControl = control; var isupport = control.ItemsSource as ISupportIncrementalLoading; if (isupport != null) { scrollBar.ValueChanged += scrollBar_ValueChanged; } } } static async void scrollBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { ScrollBar scrollBar = sender as ScrollBar; double max = (double)scrollBar.GetValue(ScrollBar.MaximumProperty); if (e.NewValue + 1 >= max) { var control = scrollBar.Tag as ItemsControl; if (control != null) { var result = await ((ISupportIncrementalLoading)control.ItemsSource).LoadMoreAsync(PAGE_SIZE); } } } public static T GetChild<T>(DependencyObject element) where T : DependencyObject { T result = null; Queue<DependencyObject> quene = new Queue<DependencyObject>(); quene.Enqueue(element); while (quene.Count > 0) { DependencyObject ele = quene.Dequeue(); int count = VisualTreeHelper.GetChildrenCount(ele); for (int i = 0; i < count; i++) { DependencyObject child = VisualTreeHelper.GetChild(ele, i); result = child as T; if (result != null) return result; quene.Enqueue(child); } } return result; } }
现在,假设我们页面上有一个需要实现学生分页加载的列表控件ListBox名字叫lstStudent,那么我们需要做以下两步就可以了:
1)在页面或者列表控件Loaded事件中设置附加属性IncrementaLoading的值为ture;
2)new一个新的Student实例赋值给lstStudent的ItemsSource。
private void LayoutRoot_Loaded(object sender, RoutedEventArgs e) { lstStudent.SetValue(IncrementalLoadingHelper.IncrementalLoadingProperty, true); } public async void LoadData() { var data = new Students(); await data.LoadMoreAsync(IncrementalLoadingHelper.PAGE_SIZE); lstStudent.ItemsSource = data; }
下拉一下,是不是可以分页加载了!