ISupportIncrementalLoading加载的项目超出了要求

时间:2017-06-26 08:09:50

标签: c# xaml uwp

在我的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>();
    }
}

1 个答案:

答案 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而不是编写我自己的实现。