C# uwp增量加载保持无限调用GetPagedItemAsync
我有一个uwp应用程序,我正在使用Windows社区工具包来实现列表中的增量加载功能 复制发行的回购样本: 如何重现问题C# uwp增量加载保持无限调用GetPagedItemAsync,c#,xaml,listview,uwp,windows-community-toolkit,C#,Xaml,Listview,Uwp,Windows Community Toolkit,我有一个uwp应用程序,我正在使用Windows社区工具包来实现列表中的增量加载功能 复制发行的回购样本: 如何重现问题 <Grid x:DefaultBindMode="OneWay"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowD
<Grid x:DefaultBindMode="OneWay">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<GridView
x:Name="TopListView"
ItemsSource="{x:Bind ViewModel.AppVM.EvidencesConfigurationList}"
SelectedItem="{x:Bind ViewModel.AppVM.TopListViewItem, Mode=TwoWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:ModulesConfigurationDto">
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind QueryCount}" />
<TextBlock
Grid.Row="1"
Text="{x:Bind DisplayName}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<ListView
x:Name="AllEvidencesListView"
Grid.Row="1"
Header="{x:Bind ViewModel.AppVM.TopListViewItem.DisplayName}"
ItemsSource="{x:Bind ViewModel.AppVM.TopListViewItem.Evidences}" />
</Grid>
<Grid x:DefaultBindMode="OneWay">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<GridView
x:Name="TopListView"
ItemsSource="{x:Bind ViewModel.AppVM.EvidencesConfigurationList}"
SelectedItem="{x:Bind ViewModel.AppVM.TopListViewItem, Mode=TwoWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:ModulesConfigurationDto">
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind QueryCount}" />
<TextBlock
Grid.Row="1"
Text="{x:Bind DisplayName}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<ListView
x:Name="AllEvidencesListView"
Grid.Row="1"
Header="{x:Bind ViewModel.AppVM.TopListViewItem.DisplayName}"
ItemsSource="{x:Bind ViewModel.AppVM.TopListViewItem.Evidences}" />
</Grid>
AppViewModel
public class AppViewModel : Observable
{
private ModulesConfigurationDto _topListViewItem;
private ObservableCollection<ModulesConfigurationDto> _evidencesConfigurationList;
public ModulesConfigurationDto TopListViewItem { get => _topListViewItem; set => Set(ref _topListViewItem, value); }
public ObservableCollection<ModulesConfigurationDto> EvidencesConfigurationList { get { if (_evidencesConfigurationList is null) { _evidencesConfigurationList = new ObservableCollection<ModulesConfigurationDto>(); } return _evidencesConfigurationList; } set => Set(ref _evidencesConfigurationList, value); }
public async Task LoadDataAsync()
{
await Task.Delay(200);//fake web api call
List<ModulesConfigurationDto> result = new List<ModulesConfigurationDto> {
new ModulesConfigurationDto { DisplayName = "RecentEvidences", ModuleCode = "TM", QueryCount = 27 },
new ModulesConfigurationDto { DisplayName = "Evidences", ModuleCode = "TM", QueryCount = 27 }};
if (result?.Count > 0)
{
EvidencesConfigurationList.Clear();
IEnumerable<ModulesConfigurationDto> evidencesConfigurationList = result.Where(a => a.ModuleCode == "TM");
foreach (ModulesConfigurationDto item in evidencesConfigurationList)
{
item.Evidences = new IncrementalLoadingCollection<EvidencesSource, EvidenceDTO>();
}
EvidencesConfigurationList.AddRange(evidencesConfigurationList);
}
}
}
公共类AppViewModel:可观察
{
私有模块配置到_topListViewItem;
私有可观察收集(U证据配置列表);
TopListViewItem的公共模块配置{get=>\u TopListViewItem;set=>set(ref _TopListViewItem,value);}
公共ObserviceCollection证据配置列表{get{if(_-EvidenceConfigurationList为null){u-EvidenceConfigurationList=new-ObserviceCollection();}返回_-EvidenceConfigurationList;}集=>set(ref _-EvidenceConfigurationList,value);}
公共异步任务LoadDataAsync()
{
等待任务。延迟(200);//伪web api调用
列表结果=新列表{
新模块配置为{DisplayName=“RecentEvidences”,ModuleCode=“TM”,QueryCount=27},
新的模块配置为{DisplayName=“证据”,ModuleCode=“TM”,QueryCount=27};
如果(结果?.Count>0)
{
证据配置列表。清除();
IEnumerable证据配置列表=结果,其中(a=>a.ModuleCode==“TM”);
foreach(模块配置到证据配置列表中的项)
{
item.Evidences=new IncrementalLoadingCollection();
}
证据配置列表.AddRange(证据配置列表);
}
}
}
证据来源(用于增量加载)
公共类证据源:IIncrementalSource
{
公共AppViewModel AppVM=>App.AppVM;
公共异步任务GetPagedItemsAsync(int-pageIndex、int-pageSize、CancellationToken CancellationToken=default)
{
var证据=新列表();
等待任务。延迟(500);//伪web api调用
证据。添加(新的证据到());
返还证据;
}
}
更新1
根据“Faywang-MSFT”的回答,我尝试了以下方法
private readonly List<EvidenceDTO> evidences;
public EvidencesSource()
{
evidences = new List<EvidenceDTO>();
for (int i = 0; i < 40; i++)
{
evidences.Add(new EvidenceDTO());
}
}
public async Task<IEnumerable<EvidenceDTO>> GetPagedItemsAsync(int pageIndex, int pageSize, CancellationToken cancellationToken = default)
{
await Task.Delay(500); // fake web api call
var result = (from dto in evidences
select dto).Skip(pageIndex * pageSize).Take(pageSize);
return result;
}
私有只读列表证据;
公共证据源()
{
证据=新列表();
对于(int i=0;i<40;i++)
{
证据。添加(新的证据到());
}
}
公共异步任务GetPagedItemsAsync(int-pageIndex、int-pageSize、CancellationToken CancellationToken=default)
{
等待任务。延迟(500);//伪web api调用
var结果=(从证据中的dto)
选择dto).Skip(页面索引*页面大小).Take(页面大小);
返回结果;
}
但它并没有解决这个问题,因为默认的pageSize值是20,所以GetPagesItemsAsync第一次运行以获取前20个项目,然后再次调用自身以获取剩余的20个项目,然后第三次再次调用自身,但这一次因为结果返回空,所以它不会再次调用,我查看UI,所有40个项目都已加载,因此,该方法一直在调用自己,直到返回一个空结果
更新2
现在,我尝试了Windows社区工具包示例应用程序中的确切示例,并在页面的下半部分放置了一个PeopleListView,它也显示了相同的错误。我也用这些代码更新了示例应用程序,你可以在页面的下半部分看到,所有40个人都被加载了两次,执行这个方法时我没有滚动它,当我滚动它时,它已经有了所有的项目,所以这次它返回0个项目
更新3
如果我将证据或人员的数量增加到200,那么它第一次运行了两次GetPagedItemAsync,因此得到了40个项目,然后它停止执行,除非我滚动,所以它工作正常。唯一关心的是,为什么它第一次得到两个页面,而它应该只得到一个页面?这对我来说是个问题,因为我从webapi获取数据,所以不能进行太多无用的调用
更新4
如果我将页面大小更改为2:
var collection = new IncrementalLoadingCollection<PeopleSource, Person>(itemsPerPage:2);
var collection=newincrementalloadingcollection(itemsPerPage:2);
然后,该方法似乎会一直调用自己,直到加载了大约40项,这大约是UI可以显示的两倍大小,即:大约18,但有趣的是,如果我将pageSize更改为40
var collection = new IncrementalLoadingCollection<PeopleSource, Person>(itemsPerPage:40);
var collection=newincrementalloadingcollection(itemsPerPage:40);
那么
var collection = new IncrementalLoadingCollection<PeopleSource, Person>(itemsPerPage:40);
public async Task<IEnumerable<EvidenceDTO>> GetPagedItemsAsync(int pageIndex, int pageSize, CancellationToken cancellationToken = default(CancellationToken))
{
var evidences = new List<EvidenceDTO>();
evidences.Add(new EvidenceDTO());
var result = (from dto in evidences
select dto).Skip(pageIndex * pageSize).Take(pageSize);
await Task.Delay(500);
return result;
}
using System;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml.Data;
namespace UwpHelpers.Controls.Common
{
public class IncrementalLoadingCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
private readonly Func<CancellationToken, uint, Task<ObservableCollection<T>>> func;
private uint maxItems;
private bool isInfinite;
private CancellationToken token;
/// <summary>
/// Infinite, incremental scrolling list supported by ListView and GridView
/// </summary>
/// <param name="func"></param>
public IncrementalLoadingCollection(Func<CancellationToken, uint, Task<ObservableCollection<T>>> func)
: this(func, 0)
{
}
/// <summary>
/// Incremental scrolling list supported by ListView and GridView
/// </summary>
/// <param name="func">Task that retrieves the items</param>
/// <param name="maxItems">Set to the maximum number of items to expect</param>
public IncrementalLoadingCollection(Func<CancellationToken, uint, Task<ObservableCollection<T>>> func, uint maxItems)
{
this.func = func;
if (maxItems == 0) //Infinite
{
isInfinite = true;
}
else
{
this.maxItems = maxItems;
isInfinite = false;
}
}
public bool HasMoreItems
{
get
{
if (this.token.IsCancellationRequested)
return false;
if (isInfinite)
return true;
return this.Count < maxItems;
}
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return AsyncInfo.Run(tkn => InternalLoadMoreItemsAsync(tkn, count));
}
private async Task<LoadMoreItemsResult> InternalLoadMoreItemsAsync(CancellationToken passedToken, uint count)
{
ObservableCollection<T> tempList = null;
this.token = passedToken;
var baseIndex = this.Count;
uint numberOfItemsToGet = 0;
if (!isInfinite)
{
if (baseIndex + count < maxItems)
{
numberOfItemsToGet = count;
}
else
{
numberOfItemsToGet = maxItems - (uint) (baseIndex);
}
}
else
{
numberOfItemsToGet = count;
}
tempList = await func(passedToken, numberOfItemsToGet);
if (tempList.Count == 0) //no more items, stop the incremental loading
{
maxItems = (uint) this.Count;
isInfinite = false;
}
else
{
foreach (var item in tempList)
{
this.Add(item);
}
}
return new LoadMoreItemsResult { Count = (uint) tempList.Count };
}
}
}