MVVM 分页和排序

Posted

技术标签:

【中文标题】MVVM 分页和排序【英文标题】:MVVM paging & sorting 【发布时间】:2011-07-15 10:06:51 【问题描述】:

我正在努力寻找合适的解决方案来为符合 MVVM P&P 的 WPF DataGrid 实现排序和分页。

以下示例说明了实现分页的有效方法,它遵循 MVVM 实践,但排序的自定义实现(一旦实现分页就需要)不遵循 MVVM:

http://www.eggheadcafe.com/tutorials/aspnet/8a2ea78b-f1e3-45b4-93ef-32b2d802ae17/wpf-datagrid-custom-pagin.aspx

我目前有一个绑定到 CollectionViewSource 的 DataGrid(在 XAML 中使用 GroupDescriptions 和 SortDescritptions 定义)绑定到我的 ViewModel 中的 ObservableCollection。一旦您通过限制 DataGrid 每页获取的项目数来实现分页,它就会破坏 CollectionViewSource 中定义的排序,因为它只对项目的子集进行排序。 MVVM下实现分页和排序的最佳方法是什么?

谢谢,

亚伦

【问题讨论】:

【参考方案1】:

前几天我写了一个PagingController 类来帮助分页,所以你去吧:

PagingController.cs CurrentPageChangedEventArgs.cs

您将不得不稍微清理一下源代码,因为使用了一些 MS 代码合同,它们引用了 Prism 的一些(非常基本的)实用程序等。

使用示例(代码隐藏 - ViewModel.cs):

private const int PageSize = 20;

private static readonly SortDescription DefaultSortOrder = new SortDescription("Id", ListSortDirection.Ascending);

private readonly ObservableCollection<Reservation> reservations = new ObservableCollection<Reservation>();

private readonly CollectionViewSource reservationsViewSource = new CollectionViewSource();

public ViewModel()

    this.reservationsViewSource.Source = this.reservations;

    var sortDescriptions = (INotifyCollectionChanged)this.reservationsViewSource.View.SortDescriptions;
    sortDescriptions.CollectionChanged += this.OnSortOrderChanged;

    // The 5000 here is the total number of reservations
    this.Pager = new PagingController(5000, PageSize);
    this.Pager.CurrentPageChanged += (s, e) => this.UpdateData();

    this.UpdateData();



public PagingController Pager  get; private set; 

public ICollectionView Reservations

    get  return this.reservationsViewSource.View; 


private void UpdateData()

    var currentSort = this.reservationsViewSource.View.SortDescriptions.DefaultIfEmpty(DefaultSortOrder).ToArray();

    // This is the "fetch the data" method, the implementation of which
    // does not directly interest us for this example.
    var data = this.crsService.GetReservations(this.Pager.CurrentPageStartIndex, this.Pager.PageSize, currentSort);
    this.reservations.Clear();
    this.reservations.AddRange(data);


private void OnSortOrderChanged(object sender, NotifyCollectionChangedEventArgs e)

    if (e.Action == NotifyCollectionChangedAction.Add) 
        this.UpdateData();
    

使用示例(XAML - View.xaml):

<DataGrid ... ItemSource="Binding Reservations" />

<!-- all the rest is UI to interact with the pager -->
<StackPanel>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="4">
        <StackPanel.Resources>
            <Style TargetType="x:Type Button">
                <Setter Property="FontFamily" Value="Webdings" />
                <Setter Property="Width" Value="60" />
                <Setter Property="Margin" Value="4,0,4,0" />
            </Style>
            <Style TargetType="x:Type TextBlock">
                <Setter Property="Margin" Value="4,0,4,0" />
                <Setter Property="VerticalAlignment" Value="Center" />
            </Style>
            <Style TargetType="x:Type TextBox">
                <Setter Property="Margin" Value="4,0,4,0" />
                <Setter Property="Width" Value="40" />
            </Style>
        </StackPanel.Resources>
        <Button Content="9" Command="Binding Path=Pager.GotoFirstPageCommand" />
        <Button Content="3" Command="Binding Path=Pager.GotoPreviousPageCommand" />
        <TextBlock Text="Page" />
        <TextBox Text="Binding Path=Pager.CurrentPage, ValidatesOnExceptions=True" />
        <TextBlock Text="Binding Path=Pager.PageCount, StringFormat=of 0" />
        <Button Content="4" Command="Binding Path=Pager.GotoNextPageCommand" />
        <Button Content=":" Command="Binding Path=Pager.GotoLastPageCommand" />
    </StackPanel>
    <ScrollBar Orientation="Horizontal" Minimum="1" Maximum="Binding Path=Pager.PageCount" Value="Binding Path=Pager.CurrentPage"/>
</StackPanel>

简短说明:

如您所见,ViewModel 并没有真正做太多事情。它保存代表当前页面的项目集合,并向视图公开CollectionView(用于数据绑定)和PagingController。然后它所做的就是在每次PagingController 表明某些事情发生变化时更新集合中的数据项(因此在CollectionView 中)。当然,这意味着您需要一个方法,给定起始索引、页面大小和SortDescription[],返回由这些参数描述的数据切片。这是你的业务逻辑的一部分,我在这里没有包含它的代码。

在 XAML 方面,所有工作都是通过绑定到 PagingController 来完成的。我在这里公开了全部功能(绑定到 First/Prev/Next/Last 命令的按钮,将TextBox 直接绑定到CurrentPage,以及将ScrollBar 绑定到CurrentPage)。通常,您不会同时使用所有这些。

【讨论】:

谢谢乔恩,非常感谢! 这是否适用于调整大小(从而改变可见元素的数量)的 DataGrid? @ChrisKlepeis:不是给定的,但你可以绑定到PageSize 并在上面注册PropertyChanged,所以我在那里看不到任何阻碍。当然,寻呼机并不是为了有效地覆盖这种情况,所以性能不会像它应该的那样好。 我明白了。我的错。谢谢。【参考方案2】:

您应该在 ViewModel 中使用 ListCollectionView 类型的集合属性,并将 Grid 绑定到它。这样,CollectionView 定义就不会位于 View 中,而是位于 ViewModel(它所属的位置)中,这将帮助您在 ViewModel 中轻松执行所有操作(无论是分页、排序还是过滤)

【讨论】:

感谢 Elad 的回复! 这是一个很好的答案。谢谢!

以上是关于MVVM 分页和排序的主要内容,如果未能解决你的问题,请参考以下文章

分页和排序

分页和排序问题

Mysql分页和排序子查询聚集函数

laravel分页和排序表

Angular Material 表的问题 - 分页和排序

在 oracle 中使用存储过程进行分页和排序