使用 ReactiveUI 的可分页实时搜索
Pageable realtime search with ReactiveUI
我有一个界面,当我需要我的视图可分页时,我将其添加到我的 ViewModel
public interface IPageable
{
Task LoadNextPage();
bool IsLoadingPage { get; set; }
}
然后我将 PageableBehavior
附加到我的 ListView
,每当我的 ListView 到达页面底部时,它就会调用 ViewModel 上的 IPageable.LoadNextPage();
。 (注意:在不使用 ReactiveUI 的情况下,我已经完成了所有这些工作)
我的问题是,虽然我可以轻松地对搜索词做出反应,但我似乎无法弄清楚如何以响应方式将下一页也加载到 _searchResults
。
public class SeedSelectionViewModel : ReactiveObject, IPageable
{
private const int PageSize = 30;
private const int Threshold = 3;
private static readonly TimeSpan _throttle = TimeSpan.FromMilliseconds(200);
private readonly ISeedInputDataService _dataService;
private readonly ObservableAsPropertyHelper<IEnumerable<SeedVarietySeed>> _searchResults;
public SeedSelectionViewModel(ISeedInputDataService dataService)
{
_dataService = dataService;
this.WhenAnyValue(x => x.SearchText)
.Throttle(_throttle)
.Select(term => term?.Trim())
.DistinctUntilChanged()
.Where(term => !string.IsNullOrWhiteSpace(term) && term.Trim().Length >= Threshold)
.SelectMany((searchText,count,token) => SearchSeedVarieties(searchText,0))
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, nameof(SearchResults), out _searchResults);
}
[Reactive] public string SearchText { get; set; }
[Reactive] public bool IsLoadingPage { get; set; }
public IEnumerable<SeedVarietySeed> SearchResults => _searchResults.Value;
private Task<IEnumerable<SeedVarietySeed>> SearchSeedVarieties(string searchText, int skip)
=> _dataService.FindSeedVarieties(searchText, skip, PageSize, CancellationToken.None);
public Task LoadNextPage()
{
// how do I load additional results into _searchResults?
return Task.CompletedTask;
}
}
这里的问题是你不能这样使用ObservableAsPropertyHelper
。您不能从两个不同的订阅中更改它。
正确的处理方法是使用 ObservableCollection<T>
或类似的方法,然后从 WhenAnyValue
调用 InvokeCommand
ctor()
{
_loadDataCommand = ReactiveCommand.CreateFromTask<string>(LoadData);
this.WhenAnyValue(x => x.SearchText)
.Throttle(_throttle)
.Select(term => term?.Trim())
.DistinctUntilChanged()
.Where(term => !string.IsNullOrWhiteSpace(term) && term.Trim().Length >= Threshold)
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(_loadDataCommand);
}
private async Task LoadData(string searchText)
{
var result = await _dataService.FindSeedVarieties(searchText, 0, PageSize, CancellationToken.None).ConfigureAwait(true);
SearchResults.ReplaceRange(result);
}
我有一个界面,当我需要我的视图可分页时,我将其添加到我的 ViewModel
public interface IPageable
{
Task LoadNextPage();
bool IsLoadingPage { get; set; }
}
然后我将 PageableBehavior
附加到我的 ListView
,每当我的 ListView 到达页面底部时,它就会调用 ViewModel 上的 IPageable.LoadNextPage();
。 (注意:在不使用 ReactiveUI 的情况下,我已经完成了所有这些工作)
我的问题是,虽然我可以轻松地对搜索词做出反应,但我似乎无法弄清楚如何以响应方式将下一页也加载到 _searchResults
。
public class SeedSelectionViewModel : ReactiveObject, IPageable
{
private const int PageSize = 30;
private const int Threshold = 3;
private static readonly TimeSpan _throttle = TimeSpan.FromMilliseconds(200);
private readonly ISeedInputDataService _dataService;
private readonly ObservableAsPropertyHelper<IEnumerable<SeedVarietySeed>> _searchResults;
public SeedSelectionViewModel(ISeedInputDataService dataService)
{
_dataService = dataService;
this.WhenAnyValue(x => x.SearchText)
.Throttle(_throttle)
.Select(term => term?.Trim())
.DistinctUntilChanged()
.Where(term => !string.IsNullOrWhiteSpace(term) && term.Trim().Length >= Threshold)
.SelectMany((searchText,count,token) => SearchSeedVarieties(searchText,0))
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, nameof(SearchResults), out _searchResults);
}
[Reactive] public string SearchText { get; set; }
[Reactive] public bool IsLoadingPage { get; set; }
public IEnumerable<SeedVarietySeed> SearchResults => _searchResults.Value;
private Task<IEnumerable<SeedVarietySeed>> SearchSeedVarieties(string searchText, int skip)
=> _dataService.FindSeedVarieties(searchText, skip, PageSize, CancellationToken.None);
public Task LoadNextPage()
{
// how do I load additional results into _searchResults?
return Task.CompletedTask;
}
}
这里的问题是你不能这样使用ObservableAsPropertyHelper
。您不能从两个不同的订阅中更改它。
正确的处理方法是使用 ObservableCollection<T>
或类似的方法,然后从 WhenAnyValue
InvokeCommand
ctor()
{
_loadDataCommand = ReactiveCommand.CreateFromTask<string>(LoadData);
this.WhenAnyValue(x => x.SearchText)
.Throttle(_throttle)
.Select(term => term?.Trim())
.DistinctUntilChanged()
.Where(term => !string.IsNullOrWhiteSpace(term) && term.Trim().Length >= Threshold)
.ObserveOn(RxApp.MainThreadScheduler)
.InvokeCommand(_loadDataCommand);
}
private async Task LoadData(string searchText)
{
var result = await _dataService.FindSeedVarieties(searchText, 0, PageSize, CancellationToken.None).ConfigureAwait(true);
SearchResults.ReplaceRange(result);
}