目录
介绍
动态数据
数据分页
填充数据源
页面切换
结论
- 下载资料库
介绍
如果您有一个大的数据集合,那么用集合中的所有数据填充项目控件就变得不切实际,更不用说用户不友好了。最好的方法是对数据进行分段,因此项目控件仅显示数据的子集,并允许用户在数据段之间循环。可以使用动态数据库在.NET应用程序中实现这种分页功能,本文将介绍如何在WPF-MVVM应用程序中实现此功能。
动态数据动态Data是一个可移植的类库,提供包含反应性扩展(Rx)功能的集合。动态数据集合可以是SourceList类型的可观察列表,也可以是SourceCache类型的可观察缓存。这些集合使用可观察到的变更集进行管理,这些变更集是通过调用集合的Connect()运算符创建的,并且可以是类型IObservable或IObservable。数据处理操作(如排序、分组、过滤、数据虚拟化和分页)是使用可链接在一起以执行复杂操作的运算符完成的。截至撰写本文时,该库有60个集合运算符。
要使用动态数据,您的项目必须引用动态数据NuGet包。
数据分页如上一节所述,动态数据提供两种类型的反应性集合,它们充当数据源。要对数据进行分页,您需要利用SourceCache集合。在示例项目中,此类集合在IEmployeesService实现中定义,并将包含Employee类型的对象。
using Bogus;
using DynamicData;
using PagedData.WPF.Models;
using System;
namespace PagedData.WPF.Services
{
public class EmployeesService : IEmployeesService
{
private readonly ISourceCache _employees;
public EmployeesService() => _employees = new SourceCache(e => e.ID);
public IObservable
EmployeesConnection() => _employees.Connect();
public void LoadData()
{
var employeesFaker = new Faker()
.RuleFor(e => e.ID, f => f.IndexFaker)
.RuleFor(e => e.FirstName, f => f.Person.FirstName)
.RuleFor(e => e.LastName, f => f.Person.LastName)
.RuleFor(e => e.Age, f => f.Random.Int(20, 60))
.RuleFor(e => e.Gender, f => f.Person.Gender.ToString());
_employees.AddOrUpdate(employeesFaker.Generate(1500));
}
}
}
在LoadData()中,通过调用集合的AddOrUpdate()方法将数据添加到可观察的缓存中。该方法有两个重载:一个重载单个对象,另一个重载对象集合。使用Bogus将1500个employee对象添加到可观察的集合中,该集合会生成20到60岁之间的employee的伪数据。
集合的可观察更改集由EmployeesConnection()公开,调用集合的Connect()运算符。然后,可以将可观察到的变更集绑定到视图模型中的ReadOnlyObservableCollection,并且还可以调用其他运算符来执行数据管理操作。
public class MainWindowViewModel : ViewModelBase
{
private const int PAGE_SIZE = 25;
private const int FIRST_PAGE = 1;
private readonly IEmployeesService _employeesService;
private readonly ISubject _pager;
private readonly ReadOnlyObservableCollection _employees;
public ReadOnlyObservableCollection Employees => _employees;
public MainWindowViewModel(IEmployeesService employeesService)
{
_employeesService = employeesService;
_pager = new BehaviorSubject(new PageRequest(FIRST_PAGE, PAGE_SIZE));
_employeesService.EmployeesConnection()
.Sort(SortExpressionComparer.Ascending(e => e.ID))
.Page(_pager)
.Do(change => PagingUpdate(change.Response))
.ObserveOnDispatcher()
.Bind(out _employees)
.Subscribe();
}
...
}
要对数据进行分页,首先必须对其进行排序。然后,您可以调用Page()运算符,该运算符采用ISubject来指定第一页以及每页中的项目数。当集合发生变化时,Do()操作符提供更新,因此我使用它来使用IPagedChangeSet响应更新几个视图模型属性。
private void PagingUpdate(IPageResponse response)
{
TotalItems = response.TotalSize;
CurrentPage = response.Page;
TotalPages = response.Pages;
}
填充数据源
当应用程序加载时,数据将被添加到反应性集合中。这是通过视图模型中的LoadDataCommand完成的。
private RelayCommand _loadDataCommand;
public RelayCommand LoadDataCommand =>
_loadDataCommand ??= new RelayCommand(_ => LoadEmployeeData());
private void LoadEmployeeData() => _employeesService.LoadData();
页面切换
数据页面之间的循环是使用先前定义的ISubject来完成的,该对象具有一个传递了PageRequest对象的OnNext()运算符。
...
#region Previous page command
private RelayCommand _previousPageCommand;
public RelayCommand PreviousPageCommand => _previousPageCommand ??=
new RelayCommand(_ => MoveToPreviousPage(), _ => CanMoveToPreviousPage());
private void MoveToPreviousPage() =>
_pager.OnNext(new PageRequest(_currentPage - 1, PAGE_SIZE));
private bool CanMoveToPreviousPage() => CurrentPage > FIRST_PAGE;
#endregion
#region Next page command
private RelayCommand _nextPageCommand;
public RelayCommand NextPageCommand => _nextPageCommand ??=
new RelayCommand(_ => MoveToNextPage(), _ => CanMoveToNextPage());
private void MoveToNextPage() =>
_pager.OnNext(new PageRequest(_currentPage + 1, PAGE_SIZE));
private bool CanMoveToNextPage() => CurrentPage < TotalPages;
#endregion
#region First page command
private RelayCommand _firstPageCommand;
public RelayCommand FirstPageCommand => _firstPageCommand ??=
new RelayCommand(_ => MoveToFirstPage(), _ => CanMoveToFirstPage());
private void MoveToFirstPage() =>
_pager.OnNext(new PageRequest(FIRST_PAGE, PAGE_SIZE));
private bool CanMoveToFirstPage() => CurrentPage > FIRST_PAGE;
#endregion
#region Last page command
private RelayCommand _lastPageCommand;
public RelayCommand LastPageCommand => _lastPageCommand ??=
new RelayCommand(_ => MoveToLastPage(), _ => CanMoveToLastPage());
private void MoveToLastPage() =>
_pager.OnNext(new PageRequest(_totalPages, PAGE_SIZE));
private bool CanMoveToLastPage() => CurrentPage < TotalPages;
#endregion
这就是分页逻辑所需要的。然后可以将视图模型的属性和命令绑定到视图中的必要元素上。
结论
希望您从本文中学到了有用的东西。如前所述,Dynamic Data具有大量的集合运算符,因此请看一下它们还可以做什么。您也可以从文章顶部的链接下载本文的示例项目。