如何对 WPF DataGrid 进行分页?

47

如何在WPF DataGrid中设置分页?


1
请在codeproject中查看此文章 - Shoban
1个回答

61
上面的代码项目文章对于使用ADO表完成此操作非常好。虽然对于大多数应用程序来说,它可能运行良好且易于理解,但还有一种更“WPF-zen-like”的方法,那就是使用CollectionViews。与上面的示例相比,使用CollectionView的优点在于它在将数据放入网格中方面更加通用(并不是说你不能使该示例更加通用),并且它与一般的WPF数据绑定模型很好地融合。如果需要这些功能,它为您提供了支持排序、分组等常见操作的位置。
我制作了一个非常简短的示例,其中包含一个仅能工作的PagingCollectionView,该视图绑定到.NET 4.0 DataGrid控件。虽然示例本身非常琐碎,但至少它向您展示了如何入手,因为您可以在实际数据集合周围拥有代理,可以执行诸如MoveToNextPage和MoveToPreviousPage之类的简单操作。
以下是窗口事件处理和PagingCollectionView的C#代码:
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;

namespace GridPagingExample
{
    public partial class MainWindow : Window
    {
        private readonly PagingCollectionView _cview;

        public MainWindow()
        {
            InitializeComponent();
            this._cview = new PagingCollectionView(
                new List<object>
                {
                    new { Animal = "Lion", Eats = "Tiger" },
                    new { Animal = "Tiger", Eats =  "Bear" },
                    new { Animal = "Bear", Eats = "Oh my" },
                    new { Animal = "Wait", Eats = "Oh my isn't an animal" },
                    new { Animal = "Oh well", Eats = "Who is counting anyway" },
                    new { Animal = "Need better content", Eats = "For posting on stackoverflow" }
                },
                2
            );
            this.DataContext = this._cview;
        }

        private void OnNextClicked(object sender, RoutedEventArgs e)
        {
            this._cview.MoveToNextPage();
        }

        private void OnPreviousClicked(object sender, RoutedEventArgs e)
        {
            this._cview.MoveToPreviousPage();
        }
    }

    public class PagingCollectionView : CollectionView
    {
        private readonly IList _innerList;
        private readonly int _itemsPerPage;

        private int _currentPage = 1;

        public PagingCollectionView(IList innerList, int itemsPerPage)
            : base(innerList)
        {
            this._innerList = innerList;
            this._itemsPerPage = itemsPerPage;
        }

        public override int Count
        {
            get 
            { 
                if (this._innerList.Count == 0) return 0;
                if (this._currentPage < this.PageCount) // page 1..n-1
                {
                    return this._itemsPerPage;
                }
                else // page n
                {
                    var itemsLeft = this._innerList.Count % this._itemsPerPage;
                    if (0 == itemsLeft)
                    {
                        return this._itemsPerPage; // exactly itemsPerPage left
                    }
                    else
                    {
                        // return the remaining items
                        return itemsLeft;
                    }
                }
            }
        }

        public int CurrentPage
        {
            get { return this._currentPage; }
            set
            {
                this._currentPage = value;
                this.OnPropertyChanged(new PropertyChangedEventArgs("CurrentPage"));
            }
        }

        public int ItemsPerPage { get { return this._itemsPerPage; } }

        public int PageCount
        {
            get 
            { 
                return (this._innerList.Count + this._itemsPerPage - 1) 
                    / this._itemsPerPage; 
            }
        }

        private int EndIndex
        {
            get
            {
                var end = this._currentPage * this._itemsPerPage - 1;
                return (end > this._innerList.Count) ? this._innerList.Count : end;
            }
        }

        private int StartIndex
        {
            get { return (this._currentPage - 1) * this._itemsPerPage; }
        }

        public override object GetItemAt(int index)
        {
            var offset = index % (this._itemsPerPage); 
            return this._innerList[this.StartIndex + offset];
        }

        public void MoveToNextPage()
        {
            if (this._currentPage < this.PageCount)
            {
                this.CurrentPage += 1;
            }
            this.Refresh();
        }

        public void MoveToPreviousPage()
        {
            if (this._currentPage > 1)
            {
                this.CurrentPage -= 1;
            }
            this.Refresh();
        }
    }
}

以下是窗口的XAML代码:

这里是窗口的XAML代码:

<Window x:Class="GridPagingExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <Label Grid.Row="0" Margin="2">
                <Label.Content>
                    <Binding Path="CurrentPage">
                        <Binding.StringFormat>Current Page: {0}</Binding.StringFormat>
                    </Binding>
                </Label.Content>
            </Label>
            <Button Content="Next" Click="OnNextClicked" Margin="2"/>
            <Button Content="Previous" Click="OnPreviousClicked" Margin="2"/>
        </StackPanel>
        <DataGrid ItemsSource="{Binding}" Grid.Row="1">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Animal" Width="*" Binding="{Binding Animal}"/>
                <DataGridTextColumn Header="Eats" Width="*" Binding="{Binding Eats}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

你可以扩展这个CollectionView以支持更多功能,一些是琐碎的,比如MoveToLastPage和MoveToFirstPage,一些则需要更多思考来确定其行为方式,例如排序。希望这对你有所帮助。

1
此外,Silverlight 也有这样一个类:http://msdn.microsoft.com/en-us/library/system.windows.data.pagedcollectionview(v=vs.95).aspx,因此使用 Reflector 查看它可能是一个非常有帮助的示例。祝你好运! - timmyl
1
它似乎不支持从CollectionView派生的内置Filter属性 - 它似乎忽略了它。 - Yushatak
2
如果您使用ListCollectionView替换基类,将会获得巨大的性能提升(并且可能会减少错误)。普通的CollectionView使用了许多已弃用的行为,并且不再建议直接实例化(请参见http://referencesource.microsoft.com/#PresentationFramework/Framework/System/Windows/Data/CollectionView.cs)。 - Yushatak
我已经使用过它,效果非常好。然而,在此之前,我也遇到了超出范围的问题,但是我从某个地方找到的解决方案解决了这个问题。但是我注意到它会禁用datagrid的默认排序功能。我该如何解决?@timmyl - wingskush
这应该是WPF中的默认控件 :-) 做得好,我稍微调整了一下布局和代码。 - Zorkind
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接