MVVM пейджинг и сортировка - PullRequest
6 голосов
/ 15 марта 2011

Я изо всех сил пытаюсь найти адекватное решение для реализации сортировки и подкачки для таблицы данных WPF, которая соответствует MVPM P & P. ​​

Следующий пример иллюстрирует эффективный способ реализации подкачки, который следует практикам MVVM, ноПользовательская реализация сортировки (которая требуется после реализации разбиения на страницы) не соответствует MVVM:

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

В настоящее время у меня есть DataGrid, привязанный к CollectionViewSource (определен в XAML с GroupDescription и SortDescritptions) привязан к ObservableCollection в моей ViewModel.Как только вы реализуете пейджинг, ограничивая количество элементов, которые ваш DataGrid получает на страницу, он нарушает сортировку, определенную в CollectionViewSource, поскольку сортирует только подмножество элементов.Каков лучший подход в MVVM для реализации подкачки и сортировки?

Спасибо,

Аарон

Ответы [ 2 ]

13 голосов
/ 15 марта 2011

На днях я написал PagingController класс, чтобы помочь с подкачкой страниц, так что вот вам:

Вам придется немного почистить источники, потому что некоторые используют MS Code Contracts, они ссылаются на некоторые (действительно базовые) утилиты из 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 для представления.Затем все, что он делает, это обновляет элементы данных в коллекции (и, следовательно, в CollectionView) каждый раз, когда PagingController указывает, что что-то изменилось.Конечно, это означает, что вам нужен метод, который с учетом начального индекса, размера страницы и SortDescription[] возвращает фрагмент данных, описанный этими параметрами.Это часть вашей бизнес-логики, и я не включил здесь код для этого.

На стороне XAML вся работа выполняется путем привязки к PagingController.Здесь я раскрыл всю функциональность (кнопки, связанные с командами First / Prev / Next / Last, прямая привязка TextBox к CurrentPage и привязка ScrollBar к CurrentPage).Обычно вы не будете использовать все это одновременно.

4 голосов
/ 15 марта 2011

Вы должны использовать свойство коллекции типа ListCollectionView в вашей ViewModel и привязать к нему Grid.Таким образом, определение CollectionView будет располагаться не во View, а во ViewModel (там, где оно принадлежит), и это поможет вам легко выполнять все необходимые манипуляции в ViewModel (будь то разбиение по страницам, сортировка или фильтрация)

...