在我的UWP中,我有一个ListView
,使用ISupportIncrementalLoading
界面逐步填充以进行无限滚动。
此列表位于PageX
页面上,只要我导航到此页面,就会填充ListView
。
这段时间有效,有时则无效。导航到PageX
时会导致问题,LoadMoreItemsAsync
被多次调用(仅限第一次,为了进一步滚动,它可以正常工作)。
这是我的代码:
public class ItemsToShow : ObservableCollection<SearchResultViewModel>, ISupportIncrementalLoading
{
private SearchResponse ResponseObject { get; set; } = new SearchResponse();
private MetadataReply Metadata { get; set; } = new MetadataReply();
SearchResultViewModel viewModel = null;
public bool HasMoreItems
{
get
{
if ((string.IsNullOrEmpty(SomeStaticClass.NextPageToken) && !SomeStaticClass.IsFirstRequest) || SomeStaticClass.StopIncrementalLoading)
return false;
if(SomeStaticClass.IsFirstRequest)
{
using (var db = new DbContext())
{
var json = db.UpdateResponse.First(r => r.LanguageId == DataStore.Language).JsonResponse;
Metadata = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateApiResponse>(json).response.metadata.reply;
}
var returnObject = SomeStaticClass.SearchResponse;
ResponseObject = returnObject.response;
}
else
{
var returnObject = new SearchApiCall().CallSearchApiAsync(
SomeStaticClass.QueryString,
SomeStaticClass.NextPageToken,
SomeStaticClass.Filters).Result;
ResponseObject = returnObject.response;
}
return ResponseObject.documents.Count > 0;
}
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
if (SomeStaticClass.IsFirstRequest) SomeStaticClass.Facet = ResponseObject.facets;
return Task.Run<LoadMoreItemsResult>(async () =>
{
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
foreach (var item in ResponseObject.documents)
{
this.Add(new SearchResultViewModel { .... });
}
});
SomeStaticClass.IsFirstRequest = false;
SomeStaticClass.NextPageToken = ResponseObject.pageToken;
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SearchResultPage.searchResultPage.FilterButton.Visibility = Visibility.Visible;
});
return new LoadMoreItemsResult() { Count = count };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
我的ListView
:
<ListView Name="SearchResultListView"
SelectionMode="Single"
IsItemClickEnabled="True"
ItemClick="SearchResultListView_ItemClick">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="ViewModels:SomeViewModel">
<Grid Style="{StaticResource SomeStyle}">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind qqq}/>
<StackPanel Grid.Row="1">
<TextBlock Text="{x:Bind xxx}"/>
<TextBlock Text="{x:Bind yyy}"/>
</StackPanel>
<StackPanel Grid.Row="2">
<TextBlock Text="{x:Bind aaa}"/>
<TextBlock Text="{x:Bind bbb}" />
</StackPanel>
<TextBlock Text="{x:Bind EducationalLevel}"/
<StackPanel Grid.Row="4">
<TextBlock Text="{x:Bind Language}"/>
<Image Source="{x:Bind ccc}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
具有此ListView
的页面的代码:
public sealed partial class SearchResultPage
{
public static SearchResultPage searchResultPage { get; private set; }
private SearchResultParameterWrapper ReceivedParameter { get; set; } = new SearchResultParameterWrapper();
public SearchResultPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
searchResultPage = this;
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode != NavigationMode.Back) FilterButton.Visibility = Visibility.Collapsed;
SomeStaticClass.IsFirstRequest = true;
SomeStaticClass.Filters = new FilterParametersWrapper();
ReceivedParameter = (SearchResultParameterWrapper)e.Parameter;
if (ReceivedParameter != null)
{
SomeStaticClass.QueryString = ReceivedParameter.QueryString;
SomeStaticClass.Filters = ReceivedParameter.Filters;
SomeStaticClass.RestoreOldFilters = ReceivedParameter.RestoreOldFilters;
if(SomeStaticClass.IsFirstRequest)
await HandleNoResult(ReceivedParameter);
}
}
private async Task HandleNoResult(SearchResultParameterWrapper parameter)
{
if (!ApiStore.IsConnected())
{
Toast.ShowToast(MainPage.mainPage.ViewModel._APP_check_network, ToastRow);
return;
}
MyProgressRing.IsActive = true;
SearchResultListView.ItemsSource = null;
SearchResultListView.Items.ToList().Clear();
SearchResponse responseObject = null;
SearchApiResponse apiResponse = null;
try
{
SomeStaticClass.StopIncrementalLoading = true;
SomeStaticClass.SearchResponse = await new SearchApiCall().CallSearchApiAsync(parameter.QueryString, null, parameter.Filters);
apiResponse = SomeStaticClass.SearchResponse;
responseObject = apiResponse.response;
if (responseObject.documents.Count <= 0)
{
NoResultsTextBlock.Visibility = Visibility.Visible;
FilterButton.Visibility = Visibility.Collapsed;
return;
}
else
{
SearchResultListView.ItemsSource = new ItemsToShow();
SomeStaticClass.StopIncrementalLoading = false;
}
}
catch
{
}
finally
{
MyProgressRing.IsActive = false;
}
}
public bool Reload() { return Reload(null); }
private bool Reload(object param)
{
System.Type type = Frame.CurrentSourcePageType;
if (Frame.BackStack.Any())
{
param = ReceivedParameter;
}
try { return Frame.Navigate(type, param); }
finally { Frame.BackStack.Remove(Frame.BackStack.Last()); }
}
}
修改:
我已经更新了我的代码。同样的问题仍然存在,并且出现了另一个问题,即页面有时变为空白(在显示项目一秒或两秒后):
public class ItemsToShow : ObservableCollection<SearchResultViewModel>, ISupportIncrementalLoading
{
private SearchResponse ResponseObject { get; set; } = new SearchResponse();
private bool hasMoreItems { get; set; } = true;
public bool HasMoreItems
{
set
{
hasMoreItems = value;
}
get
{
if (SomeCondition) return false;
return hasMoreItems;
}
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
Task.Delay(10);
return Task.Run<LoadMoreItemsResult>(async () =>
{
if (IsFirstRequest)
{
HasMoreItems = string.IsNullOrEmpty(ResponseObject.someProperty) ? false : true;
IsFirstRequest = false;
}
else
{
ResponseObject = await new SomeClass().SomeMethod();
HasMoreItems = string.IsNullOrEmpty(ResponseObject.someProperty) ? false : true;
}
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
foreach (var item in ResponseObject.documents)
{
this.Add(PrepareViewModel(item));
}
});
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SearchResultPage.searchResultPage.FilterButton.Visibility = Visibility.Visible;
});
return new LoadMoreItemsResult() { Count = count };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
答案 0 :(得分:3)
只要您不尝试两次加载相同的商品,我就不会发现它存在太多问题。它就像在加载时有两组数据,然后在滚动时加载一组数据。
但是你可能想知道为什么要这样做,这就是我理解它的方式。
在初始加载期间,ISupportIncrementalLoading.LoadMoreItemsAsync
将始终被多次调用,除非没有数据填充。这种行为很可能是由ListView
虚拟化的设计引起的。
我的猜测是,在ListView
决定最初加载的项目数之前,需要知道项目高度才能计算要虚拟化的项目数。因此,它首先加载一个项目以获得高度,进行计算并再次调用LoadMoreItemsAsync
。这就是为什么如果在LoadMoreItemsAsync
中放置断点,您将看到count
值在第一次调用该方法时始终为您提供1
。
为了避免这种行为,您不能简单地忽略第一次通话,因为当视野中没有项目时,不会触发增量加载。因此,你可以在黑客中忽略第一次呼叫而不是第二次呼叫。
从 UWP社区工具包中获取source,例如,您可以执行以下操作 -
private bool? _firstLoadJustPassed;
private async Task<LoadMoreItemsResult> LoadMoreItemsAsync(uint count, CancellationToken cancellationToken)
{
if (_firstLoadJustPassed == true)
{
_firstLoadJustPassed = false;
return new LoadMoreItemsResult { Count = 0 };
}
uint resultCount = 0;
_cancellationToken = cancellationToken;
try
{
if (!_cancellationToken.IsCancellationRequested)
{
IEnumerable<IType> data = null;
try
{
IsLoading = true;
data = await LoadDataAsync(_cancellationToken);
}
catch (OperationCanceledException)
{
// The operation has been canceled using the Cancellation Token.
}
catch (Exception ex) when (OnError != null)
{
OnError.Invoke(ex);
}
if (data != null && data.Any() && !_cancellationToken.IsCancellationRequested)
{
resultCount = (uint)data.Count();
foreach (var item in data)
{
Add(item);
}
if (!_firstLoadJustPassed.HasValue)
{
_firstLoadJustPassed = true;
}
}
else
{
HasMoreItems = false;
}
}
}
finally
{
IsLoading = false;
if (_refreshOnLoad)
{
_refreshOnLoad = false;
await RefreshAsync();
}
}
return new LoadMoreItemsResult { Count = resultCount };
}
可能有比这更优雅的解决方案但是再次像我说的那样,我个人没有看到这种行为的大问题,我会使用工具包中的IncrementalLoadingCollection
而不是编写我自己的实现。