在 Windows 8 Store Apps 開發中,當面對大量數據時,一次性加載所有項目會嚴重影響效能與記憶體使用。透過實作 ISupportIncrementalLoading 介面,我們可以讓 ListViewBase(包含 ListView 與 GridView)支援「增量加載」,隨著使用者滾動動態載入資料,極大提升使用者體驗。
ISupportIncrementalLoading 定義了兩個關鍵成員:
LoadMoreItemsResult(包含實際載入筆數 Count)。以下實作一個泛型集合類,繼承 ObservableCollection<T> 並實作 ISupportIncrementalLoading,可重複用於各類資料源:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
namespace XamlDemo.Binding
{
public class MyIncrementalLoading<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
private bool _isBusy = false;
private Func<int, int, List<T>> _funcGetData;
private uint _totalCount = 0;
public MyIncrementalLoading(uint totalCount, Func<int, int, List<T>> getDataFunc)
{
_funcGetData = getDataFunc;
_totalCount = totalCount;
}
public bool HasMoreItems
{
get { return this.Count < _totalCount; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
if (_isBusy)
throw new InvalidOperationException("正在加載中,請稍候...");
_isBusy = true;
var dispatcher = Window.Current.Dispatcher;
return AsyncInfo.Run(async (token) =>
{
try
{
// 模擬網路/資料庫延遲
await Task.Delay(1000);
int startIndex = this.Count;
List<T> items = null;
// 必須在 UI 執行緒加入集合,以觸發介面更新
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
items = _funcGetData(startIndex, (int)count);
foreach (var item in items)
this.Add(item);
});
return new LoadMoreItemsResult { Count = (uint)items.Count };
}
finally
{
_isBusy = false;
}
});
}
}
}
在 ListView 上透過三個屬性微調加載行為:
Edge(預設,滾動到底部觸發)或 None(停用)。<Page x:Class="XamlDemo.Binding.IncrementalLoading"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Binding"
xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<Grid Margin="120 0 0 10">
<TextBlock Name="lblMsg" FontSize="14.667" />
<ListView x:Name="listView" Width="300" Height="300"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0 30 0 0">
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="Blue" Width="200" CornerRadius="3"
HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}" FontSize="14.667" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Name="lblLog" FontSize="14.667" Margin="0 350 0 0" />
</Grid>
</Grid>
</Page>
using Windows.UI.Xaml.Controls;
using XamlDemo.Model;
using System.Linq;
using System.Collections.Specialized;
using System;
namespace XamlDemo.Binding
{
public sealed partial class IncrementalLoading : Page
{
private MyIncrementalLoading<Employee> _employees;
public IncrementalLoading()
{
this.InitializeComponent();
this.Loaded += IncrementalLoading_Loaded;
}
void IncrementalLoading_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
// 設定增量加載參數
listView.IncrementalLoadingTrigger = IncrementalLoadingTrigger.Edge;
listView.DataFetchSize = 4.0;
listView.IncrementalLoadingThreshold = 2.0;
// 建立資料源:總共 1000 筆,分頁載入
_employees = new MyIncrementalLoading<Employee>(1000, (startIndex, count) =>
{
lblLog.Text += string.Format("從索引 {0} 開始獲取 {1} 條資料\n", startIndex, count);
return TestData.GetEmployees().Skip(startIndex).Take(count).ToList();
});
_employees.CollectionChanged += _employees_CollectionChanged;
listView.ItemsSource = _employees;
}
void _employees_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
lblMsg.Text = "已獲取資料筆數:" + _employees.Count;
}
}
}
假設 ListView 每頁可顯示 6~7 筆資料:
4.0 × 1 = 4 筆。4.0 × (6 或 7) 筆。2.0 × (6 或 7) 筆時,啟動下一輪加載。透過 ISupportIncrementalLoading 與 MyIncrementalLoading<T> 封裝,開發者只需專注於「如何取得分頁資料」,即可輕鬆實現流暢、高效能的無限滾動列表。此模式適用於所有繼承自 ListViewBase 的控制項。