2011-03-14 28 views
5

estoy luchando por encontrar una solución adecuada al impletmenting Sorting y de paginación para un DataGrid WPF que se ajusta a la MVVM P & P.MVVM paginación y la clasificación

El ejemplo siguiente ilustra una forma efectiva para implementar la paginación que sigue MVVM prácticas, pero la implementación personalizada de clasificación (que se requiere una vez que se implementa paginación) no sigue MVVM:

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

que actualmente tiene una cuadrícula de datos unido a un CollectionViewSource (definido en XAML con GroupDescriptions y SortDescritpt iones) vinculados a una ObservableCollection en mi ViewModel. Tan pronto como implemente Paginación al limitar el número de elementos que su DataGrid obtiene por página, se rompe la clasificación definida en CollectionViewSource porque solo está ordenando el subconjunto de elementos. ¿Cuál es el mejor enfoque bajo MVVM para implementar Paginación y Clasificación?

Gracias,

Aaron

Respuesta

12

The otro día escribí una clase PagingController para ayudar con la paginación, así que aquí voy:

va a tener que limpiar las fuentes un poco b ebido al hacer algún uso de los contratos MS Código, que hacen referencia a algunas cosas de utilidad (muy básico) del prisma, etc.

Ejemplo de uso (código subyacente - 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(); 
    } 
} 

Ejemplo de uso (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> 

Breve explicación:

Como ve, el ViewModel realmente no hace mucho. Mantiene una colección de elementos que representan la página actual y expone un CollectionView (para el enlace de datos) y un PagingController en la Vista. Entonces todo lo que hace es actualizar los elementos de datos en la colección (y por consiguiente en el CollectionView) cada vez que el PagingController indica que algo ha cambiado. Por supuesto, esto significa que necesita un método que, dado un índice inicial, un tamaño de página, y un SortDescription[] devuelve la porción de datos descrita por estos parámetros. Esto es parte de su lógica comercial, y no he incluido código para eso aquí.

En el lado XAML todo el trabajo se realiza mediante el enlace al PagingController.He expuesto la funcionalidad completa aquí (botones vinculados a los comandos Primero/Anterior/Siguiente/Último, enlace directo de TextBox a CurrentPage y enlace de ScrollBar a). Por lo general, no usarás todo esto al mismo tiempo.

+0

¡Gracias Jon, muy apreciado! – Aaron

+0

¿Funcionaría esto para un DataGrid que cambia el tamaño (y por lo tanto cambia la cantidad de elementos visibles? –

+0

@ChrisKlepeis: no como se da pero puede vincularse a 'PageSize' y registrarse para' PropertyChanged', así que no veo cualquier cosa que se destaque allí. Por supuesto, el buscapersonas no estaba destinado a cubrir eficazmente este caso, por lo que perf no será tan bueno como podría ser. – Jon

4

se debe utilizar una propiedad de colección de tipo ListCollectionView en su modelo de vista, y obligar a la rejilla a la misma. De esta forma, la definición de CollectionView no se situaría en la Vista, sino en el ViewModel (donde pertenece) y eso le ayudaría a realizar todas las manipulaciones que desee fácilmente en ViewModel (ya sea paginación, clasificación o filtrado)

+2

Gracias por la respuesta Elad! – Aaron

+1

Esta es una gran respuesta. ¡Gracias! –

Cuestiones relacionadas