分页 WPF DataGrid
使用 DataTable 和 LINQ 查询对 WPF DataGrid 进行简单易懂的分页。
引言
我需要一种方法来分页显示来自 DataTable
的信息到 DataGrid
中。我搜索了一下,在 CodeProject 上找到了以下文章。
这些方法效果还可以,但是当我构建它们时,它们有点容易出错,并且不总是按预期响应。它们的设计方式类似于 WinForms,并且代码库相当复杂,一开始很难弄清楚正在发生什么。我需要一种与我们当前构建兼容的方法,并且能够在所有情况下可靠运行。
如果您是 C# 的新手,我建议您尝试上面的示例并手动编写代码。它们充分利用了switch
和case
语句,这些语句并不经常见到。
您可以在这里克隆我的(可怕的)优美的示例的 git 仓库
背景
我正在开发一个企业应用程序,我有一个非常大的 IList
对象,它被加载到内存中并显示在 DataGrid
中。我们希望 DataGrid
显示几百个列表项,而不是现在输出的数十万个。
我上面找到的示例在处理列表方面有奇怪的方法,有时根本不起作用,当列表不是 10 的倍数时。另外,输出需要显示项目数并且完全准确。我通过从这些示例中抽象核心概念并构建我自己的类来处理整个过程来解决了这个问题。
构建示例
这个示例非常简单易于构建。Git 仓库有详细的注释。
让我们开始构建吧!
首先,打开一个新的 Visual Studios WPF 空白应用程序。
在解决方案资源管理器中,右键单击您的项目,然后单击添加新项。
选择类文件并将其命名为 StudentModel.cs。
在 StudentModel.cs 文件中,添加以下代码以创建一个简单的通用 student
类。
public class Student
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
然后在同一个文件中,我们添加了一种方法来填充具有一些随机数据的列表。
public IList<Student> GetData()
{
List<Student> genericList = new List<Student>();
Student studentObj;
Random randomObj = new Random();
for (int i = 0; i < 12345; i++) //You can make this number anything
//you can think of (and your processor can handle).
{
studentObj = new Student
{
FirstName = "first " + i,
MiddleName = "Middle " + i,
LastName = "Last " + i,
Age = (int)randomObj.Next(1, 100)
};
genericList.Add(studentObj);
}
return genericList;
}
Result
应该看起来像这样。
using System;
using System.Collections.Generic;
namespace YourProjectNameSpace
{
class StudentModel
{
public class Student
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public IList<Student> GetData()
{
List<Student> genericList = new List<Student>();
Student studentObj;
Random randomObj = new Random();
for (int i = 0; i < 12345; i++) //You can make this number anything
//you can think of (and your processor can handle).
{
studentObj = new Student
{
FirstName = "first " + i,
MiddleName = "Middle " + i,
LastName = "Last " + i,
Age = (int)randomObj.Next(1, 100)
};
genericList.Add(studentObj);
}
return genericList;
}
}
}
Class
和 list
方法是从这篇文章借用的。
这就是我们的 Student Class 模型。让我们开始构建分页系统。
再次在 Visual Studio 中右键单击您的项目,然后单击添加 > 新项。
选择类文件并将其命名为 Paging.cs。
在 Paging.cs 文件中,确保在顶部有以下 using
语句。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
您的文件现在应该看起来像这样。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace CodeProjectDemonstration
{
class Paging
{
}
}
在 Paging
类内部,我们将执行一些特定的过程。我们将设置一个 int
PageIndex
,准备一个 DataTable
并查询我们的列表。
让我们从 Page
索引和 DataTable
开始。将它们放在 Paging
类的大括号中。
class Paging
{
public int PageIndex { get; set; }
DataTable PagedList = new DataTable();
}
现在是整个操作中最难的部分。我们需要接收一个 IList<T>
并将其查询下来,只获取我们想要的页面记录,然后将结果转换为 DataTable
。
首先,在上面创建的 DataTable
下面的大括号内,创建一个名为 SetPaging
的 public DataTable
并提供以下参数。
public DataTable SetPaging(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
}
在这里,我们设置了参数 IList<StudentModel.Student>
称为 ListToPage
和一个 int
称为 RecordsPerPage
,我们稍后会获取它。我们传递上面我们创建的 Student
类中完全限定的类名的原因是,随着我们继续前进,它将变得清晰。
现在,我们将设置两个 SetPaging
函数需要的内部变量。
int PageGroup = PageIndex * RecordsPerPage;
IList<StudentModel.Student> PagedList = new List<StudentModel.Student>();
一个 int
称为 PageGroup
,我们通过获取我们之前创建的当前 PageIndex
,乘以我们想要的 RecordsPerPage
来确定它,并且我们还创建一个新列表的实例。
结果
public DataTable SetPaging(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
int PageGroup = PageIndex * RecordsPerPage;
IList<StudentModel.Student> PagedList = new List<StudentModel.Student>();
}
现在我们需要设置 LINQ 查询,对查询做一些工作,最后返回我们的 DataTable
。
最后几部分的代码看起来像这样。
PagedList = ListToPage.Skip(PageGroup).Take(RecordsPerPage).ToList();
DataTable FinalPaging = PagedTable(PagedList);
return FinalPaging;
在这里,我们获取 ListToPage
并对其执行 LINQ 查询,以仅获取指定索引的行数。Skip(PageGroup)
将跳过传递给它的列表中的项目数。Take(RecordsPerPage)
将只给我们请求的记录数,而 ToList()
只是将所有内容整齐地放入我们的 PageList
中。
结果
public DataTable SetPaging(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
int PageGroup = PageIndex * RecordsPerPage;
IList<StudentModel.Student> PagedList = new List<StudentModel.Student>();
PagedList = ListToPage.Skip(PageGroup).Take(RecordsPerPage).ToList();
DataTable FinalPaging = PagedTable(PagedList);
return FinalPaging;
}
现在来解决那个波浪线。要获取 DataTable
,我们必须使用下一个函数将 PagedList
转换为 DataTable
。
在最后一个 SetPaging
大括号下面,添加这个。
private DataTable PagedTable<T>(IList<T> SourceList)
{
}
在这里,我们唯一的参数是从上一个函数作为 PagedList
传递进来的 List
。
要转换它,我们需要做一些非常具体的工作。首先,我们必须定义 DataTable
的列。我们可以通过读取传入列表的 Type
来做到这一点。我们还需要创建一个新的 DataTable
来存储我们的新信息。
private DataTable PagedTable<T>(IList<T> SourceList)
{
Type columnType = typeof(T);
DataTable TableToReturn = new DataTable();
}
要获取列,我们可以遍历我们之前设置的 columnType
属性。
foreach (var Column in columnType.GetProperties())
{
TableToReturn.Columns.Add(Column.Name, Column.PropertyType);
}
在这里,我们遍历属性类型,并通过 Column.Name
属性设置我们的列名,并通过 Column.PropertyType
获取列的类型。
现在我们需要获取我们 LINQ 查询返回的实际行。我们可以通过另一个循环来做到这一点。
foreach (object item in SourceList)
{
DataRow ReturnTableRow = TableToReturn.NewRow();
foreach (var Column in columnType.GetProperties())
{
ReturnTableRow[Column.Name] = Column.GetValue(item);
}
TableToReturn.Rows.Add(ReturnTableRow);
}
我们使用 [Column.Name]
属性 Tag
来设置每行值在列中的位置。
最后,我们将 TableToReturn DataTable
返回给我们的调用函数。整个代码块看起来像这样。
private DataTable PagedTable<T>(IList<T> SourceList)
{
Type columnType = typeof(T);
DataTable TableToReturn = new DataTable();
foreach (var Column in columnType.GetProperties())
{
TableToReturn.Columns.Add(Column.Name, Column.PropertyType);
}
foreach (object item in SourceList)
{
DataRow ReturnTableRow = TableToReturn.NewRow();
foreach (var Column in columnType.GetProperties())
{
ReturnTableRow[Column.Name] = Column.GetValue(item);
}
TableToReturn.Rows.Add(ReturnTableRow);
}
return TableToReturn;
}
我们的代码文件现在应该看起来像这样。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace CodeProjectDemonstration
{
class Paging
{
public int PageIndex { get; set; }
DataTable PagedList = new DataTable();
public DataTable SetPaging(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
int PageGroup = PageIndex * RecordsPerPage;
IList<StudentModel.Student> PagedList = new List<StudentModel.Student>();
PagedList = ListToPage.Skip(PageGroup).Take(RecordsPerPage).ToList();
DataTable FinalPaging = PagedTable(PagedList);
return FinalPaging;
}
private DataTable PagedTable<T>(IList<T> SourceList)
{
Type columnType = typeof(T);
DataTable TableToReturn = new DataTable();
foreach (var Column in columnType.GetProperties())
{
TableToReturn.Columns.Add(Column.Name, Column.PropertyType);
}
foreach (object item in SourceList)
{
DataRow ReturnTableRow = TableToReturn.NewRow();
foreach (var Column in columnType.GetProperties())
{
ReturnTableRow[Column.Name] = Column.GetValue(item);
}
TableToReturn.Rows.Add(ReturnTableRow);
}
return TableToReturn;
}
}
}
如果我们现在设置 DataGrid
并调用 SetPaging
函数,我们将获得一个返回的 DataTable
,其中包含任何默认的 PageIndex
和 RecordsPerPage
,但我们要先对这些数据进行 Page
。
因此,要开始分页数据,我们将简单地设置四个单独的函数:Next
、Previous
、First
和 Last
。
Next
函数看起来像这样。
public DataTable Next(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex++;
if (PageIndex >= ListToPage.Count / RecordsPerPage)
{
PageIndex = ListToPage.Count / RecordsPerPage;
}
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
我们传递了与 SetPaging
函数相同的参数,将 PageIndex
增加一,检查以确保它永远不会高于我们的 ListCount
除以我们的 RecordsPerPage
,然后调用 SetPaging
函数并将参数传递过去。
Previous
函数类似。
public DataTable Previous(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex--;
if(PageIndex <= 0)
{
PageIndex = 0;
}
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
在这里,我们将 PageIndex
减一,确保它永远不会小于零,然后调用 SetPaging
函数,并将参数传递过去。
First 和 Last 函数做同样的事情,只是方向不同。
public DataTable First(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex = 0;
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable Last(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex = ListToPage.Count / RecordsPerPage;
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
所以整个文件现在应该看起来像这样。
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace CodeProjectDemonstration
{
class Paging
{
public int PageIndex { get; set; }
DataTable PagedList = new DataTable();
public DataTable SetPaging(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
int PageGroup = PageIndex * RecordsPerPage;
IList<StudentModel.Student> PagedList = new List<StudentModel.Student>();
PagedList = ListToPage.Skip(PageGroup).Take(RecordsPerPage).ToList();
DataTable FinalPaging = PagedTable(PagedList);
return FinalPaging;
}
private DataTable PagedTable<T>(IList<T> SourceList)
{
Type columnType = typeof(T);
DataTable TableToReturn = new DataTable();
foreach (var Column in columnType.GetProperties())
{
TableToReturn.Columns.Add(Column.Name, Column.PropertyType);
}
foreach (object item in SourceList)
{
DataRow ReturnTableRow = TableToReturn.NewRow();
foreach (var Column in columnType.GetProperties())
{
ReturnTableRow[Column.Name] = Column.GetValue(item);
}
TableToReturn.Rows.Add(ReturnTableRow);
}
return TableToReturn;
}
public DataTable Next(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex++;
if (PageIndex >= ListToPage.Count / RecordsPerPage)
{
PageIndex = ListToPage.Count / RecordsPerPage;
}
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable Previous(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex--;
if (PageIndex <= 0)
{
PageIndex = 0;
}
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable First(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex = 0;
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable Last(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex = ListToPage.Count / RecordsPerPage;
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
}
}
这就是我们完成分页所需的所有内容。现在,我们将设置我们的 MainWindow
,并且为了本示例的目的,我们将在代码隐藏中进行一些初始化。
构建 MainWindow
如果您是 WPF 的常客,这些概念应该很简单,您可以直接跳到初始化。否则,我们将首先构建我们的 XAML 文件。
我有点懒,所以我将我的页面分成两个网格,一个有五行五列。
<Window x:Class="CodeProjectDemonstration.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CodeProjectDemonstration"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
</Grid>
</Grid>
</Window>
这应该为我们提供一个简单易懂的方法来布局我们的演示页面。
首先添加 DataGrid
并设置其 x:Name = 'dataGrid'
(就像我说的,懒)。
<DataGrid x:Name='dataGrid'
Grid.Column='1'
Grid.Row='1'
Grid.RowSpan='3'
Grid.ColumnSpan='3' Margin='8'/>
现在,我们将变得更懒,并添加一个 StackPanel
来放置所有按钮和 PageNumber
输出。
按钮
<StackPanel Grid.Row='1'
Grid.RowSpan='3'>
<Button Content='Next'
x:Name='NextButton'
Margin='6' />
<Button Content='Previous'
x:Name='PreviousButton'
Margin='6' />
<Button Content='First'
x:Name='FirstButton'
Margin='6' />
<Button Content='Last'
x:Name='LastButton'
Margin='6' />
</StackPanel>
Label
和 ComboBox
(我将它们水平放置,这样我看起来不完全像个失败者)。
<StackPanel Orientation='Horizontal'
HorizontalAlignment='Right'>
<Label x:Name='PageInfo' />
<ComboBox x:Name='NumberOfRecords' />
</StackPanel>
总页数
<Window x:Class="CodeProjectDemonstration.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CodeProjectDemonstration"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name='dataGrid'
Grid.Column='1'
Grid.Row='1'
Grid.RowSpan='3'
Grid.ColumnSpan='3'
Margin='8' />
<StackPanel Grid.Row='1'
Grid.RowSpan='3'>
<Button Content='Next'
x:Name='NextButton'
Margin='6' />
<Button Content='Previous'
x:Name='PreviousButton'
Margin='6' />
<Button Content='First'
x:Name='FirstButton'
Margin='6' />
<Button Content='Last'
x:Name='LastButton'
Margin='6' />
<StackPanel Orientation='Horizontal'
HorizontalAlignment='Right'>
<Label x:Name='PageInfo' />
<ComboBox x:Name='NumberOfRecords' />
</StackPanel>
</StackPanel>
</Grid>
</Grid>
</Window>
在这一点上,数据绑定是简化代码和在关注点分离内工作的最佳途径。在这个示例中,我们不会这样做。如果您不知道数据绑定是什么并且想开始学习,请遵循此链接。
初始化!
所以我们将从初始化所有必要的变量开始,并调用我们的函数。所有这些都来自代码隐藏文件(我知道,革命性的)。
确保您至少有这些 using
语句。
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows;
using System.Windows.Controls;
您的文件应该如此。
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows;
using System.Windows.Controls;
namespace CodeProjectDemonstration
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
在 public MainWindow()
函数之前,添加以下内容。
private int numberOfRecPerPage;
static Paging PagedTable = new Paging();
static StudentModel StudentList = new StudentModel();
IList<StudentModel.Student> myList = StudentList.GetData();
这将整洁地初始化我们的列表以供使用,并使我们能够访问我们之前创建的 Paging
类。
现在在 MainWindow()
中,添加以下内容。
public MainWindow()
{
InitializeComponent();
PagedTable.PageIndex = 1; //Sets the Initial Index to a default value
int[] RecordsToShow = { 10, 20, 30, 50, 100 }; //This Array can be any number of groups
foreach (int RecordGroup in RecordsToShow)
{
NumberOfRecords.Items.Add(RecordGroup); //Fill the ComboBox with the Array
}
NumberOfRecords.SelectedItem = 10; //Initialize the ComboBox
numberOfRecPerPage = Convert.ToInt32
(NumberOfRecords.SelectedItem); //Convert the Combox Output to type int
DataTable firstTable = PagedTable.SetPaging(myList, numberOfRecPerPage); //Fill a
//DataTable with the First set based on the numberOfRecPerPage
dataGrid.ItemsSource = firstTable.DefaultView; //Fill the dataGrid
//with the DataTable created previously
}
这里重要的部分是 DataGrid
的首次初始化。如您所见,我们直接调用 SetPaging
函数并传入默认值。然后我们将返回的 DataTable
分配给 DataGrid
。另一个需要注意的地方是,我们在 Paging
类中将 PageIndex
初始化为 1
。
您的整个文件现在应该看起来像这样。
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows;
using System.Windows.Controls;
namespace CodeProjectDemonstration
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int numberOfRecPerPage;
static Paging PagedTable = new Paging();
static StudentModel StudentList = new StudentModel();
IList<StudentModel.Student> myList = StudentList.GetData();
public MainWindow()
{
InitializeComponent();
PagedTable.PageIndex = 1;
int[] RecordsToShow = { 10, 20, 30, 50, 100 };
foreach (int RecordGroup in RecordsToShow)
{
NumberOfRecords.Items.Add(RecordGroup);
}
NumberOfRecords.SelectedItem = 10;
numberOfRecPerPage = Convert.ToInt32(NumberOfRecords.SelectedItem);
DataTable firstTable = PagedTable.SetPaging(myList, numberOfRecPerPage);
dataGrid.ItemsSource = firstTable.DefaultView;
}
}
}
我懂,太美了。
现在,在 MainWindow()
的最后一个大括号正下方,我们将添加一小段代码,用于显示我们显示的记录数和记录总数。
public string PageNumberDisplay()
{
int PagedNumber = numberOfRecPerPage * (PagedTable.PageIndex + 1);
if (PagedNumber > myList.Count)
{
PagedNumber = myList.Count;
}
return "Showing " + PagedNumber + " of " + myList.Count; //This dramatically
//reduced the number of times I had to write this string statement
}
在这里,我们根据记录数和页码计算我们在列表中的位置。如果该数字大于我们的列表,我们只返回列表号。这使得此计数器始终准确,无论我们在列表中的哪个位置。
现在是大家翘首以盼的部分……按钮!
回到您的 MainWindow.XAML 文件,双击每个按钮和 ComboBox
来连接它们。然后它们应该与您下面看到的一致。
private void NextButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Next(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void PreviousButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Previous(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void FirstButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.First(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void LastButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Last(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void NumberOfRecords_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
numberOfRecPerPage = Convert.ToInt32(NumberOfRecords.SelectedItem);
dataGrid.ItemsSource = PagedTable.First(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
所以这里有四个按钮和一个 ComboBox。对于每个按钮,我们调用所需的方法并将列表和要显示的记录数传递过去。对于 ComboBox
,我们调用 SelectionChanged
方法来更新 numberOfRecPerPage
,然后我们重新分页表。我必须使用第一个方法,因为如果我将其更改为每页 100 条记录,表格会从例如 30 跳到 300。因此,为了保持 UI 的整洁和用户交互的一致性,当我更改每页记录数时,我基本上会重置视图。如果您找到一种不出现这种跳转的方法,请在评论中发布!
结果,您的 MainWindow.XAML 文件将看起来像。
<Window x:Class="CodeProjectDemonstration.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CodeProjectDemonstration"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name='dataGrid'
Grid.Column='1'
Grid.Row='1'
Grid.RowSpan='3'
Grid.ColumnSpan='3'
Margin='8' />
<StackPanel Grid.Row='1'
Grid.RowSpan='3'>
<Button Content='Next'
x:Name='NextButton'
Margin='6'
Click='NextButton_Click' />
<Button Content='Previous'
x:Name='PreviousButton'
Margin='6'
Click='PreviousButton_Click' />
<Button Content='First'
x:Name='FirstButton'
Margin='6'
Click='FirstButton_Click' />
<Button Content='Last'
x:Name='LastButton'
Margin='6'
Click='LastButton_Click' />
<StackPanel Orientation='Horizontal'
HorizontalAlignment='Right'>
<Label x:Name='PageInfo' />
<ComboBox x:Name='NumberOfRecords'
SelectionChanged='NumberOfRecords_SelectionChanged' />
</StackPanel>
</StackPanel>
</Grid>
</Grid>
</Window>
代码隐藏文件
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows;
using System.Windows.Controls;
namespace CodeProjectDemonstration
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int numberOfRecPerPage; //Initialize our Variable, Classes and the List
static Paging PagedTable = new Paging();
static StudentModel StudentList = new StudentModel();
IList<StudentModel.Student> myList = StudentList.GetData();
public MainWindow()
{
InitializeComponent();
PagedTable.PageIndex = 1; //Sets the Initial Index to a default value
int[] RecordsToShow = { 10, 20, 30, 50, 100 }; //This Array can be any number groups
foreach (int RecordGroup in RecordsToShow)
{
NumberOfRecords.Items.Add(RecordGroup); //Fill the ComboBox with the Array
}
NumberOfRecords.SelectedItem = 10; //Initialize the ComboBox
numberOfRecPerPage = Convert.ToInt32(NumberOfRecords.SelectedItem); //Convert the
//Combobox Output to type int
DataTable firstTable = PagedTable.SetPaging(myList, numberOfRecPerPage); //Fill a
//DataTable with the First set based on the numberOfRecPerPage
dataGrid.ItemsSource = firstTable.DefaultView; //Fill the dataGrid with the
//DataTable created previously
}
public string PageNumberDisplay()
{
int PagedNumber = numberOfRecPerPage * (PagedTable.PageIndex + 1);
if (PagedNumber > myList.Count)
{
PagedNumber = myList.Count;
}
return "Showing " + PagedNumber + " of " + myList.Count; //This dramatically
//reduced the number of times I had to write this string statement
}
private void NextButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Next(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void PreviousButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Previous(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void FirstButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.First(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void LastButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Last(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void NumberOfRecords_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
numberOfRecPerPage = Convert.ToInt32(NumberOfRecords.SelectedItem);
dataGrid.ItemsSource = PagedTable.First(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
}
}
您都准备好了!构建并运行分页系统。
它应该看起来非常漂亮。
供您参考的全套代码。
StudentModel.cs
using System;
using System.Collections.Generic;
namespace CodeProjectDemonstration
{
class StudentModel
{
public class Student
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
public IList<Student> GetData()
{
List<Student> genericList = new List<Student>();
Student studentObj;
Random randomObj = new Random();
for (int i = 0; i < 12345; i++) //You can make this number anything
//you can think of (and your processor can handle).
{
studentObj = new Student
{
FirstName = "first " + i,
MiddleName = "Middle " + i,
LastName = "Last " + i,
Age = (int)randomObj.Next(1, 100)
};
genericList.Add(studentObj);
}
return genericList;
}
}
}
Paging.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace CodeProjectDemonstration
{
class Paging
{
public int PageIndex { get; set; }
DataTable PagedList = new DataTable();
public DataTable SetPaging(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
int PageGroup = PageIndex * RecordsPerPage;
IList<StudentModel.Student> PagedList = new List<StudentModel.Student>();
PagedList = ListToPage.Skip(PageGroup).Take(RecordsPerPage).ToList(); //This is
//where the Magic Happens. If you have a Specific sort or want to return
//ONLY a specific set of columns, add it to this LINQ Query.
DataTable FinalPaging = PagedTable(PagedList);
return FinalPaging;
}
private DataTable PagedTable<T>(IList<T> SourceList)
{
Type columnType = typeof(T);
DataTable TableToReturn = new DataTable();
foreach (var Column in columnType.GetProperties())
{
TableToReturn.Columns.Add(Column.Name, Column.PropertyType);
}
foreach (object item in SourceList)
{
DataRow ReturnTableRow = TableToReturn.NewRow();
foreach (var Column in columnType.GetProperties())
{
ReturnTableRow[Column.Name] = Column.GetValue(item);
}
TableToReturn.Rows.Add(ReturnTableRow);
}
return TableToReturn;
}
public DataTable Next(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex++;
if (PageIndex >= ListToPage.Count / RecordsPerPage)
{
PageIndex = ListToPage.Count / RecordsPerPage;
}
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable Previous(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex--;
if (PageIndex <= 0)
{
PageIndex = 0;
}
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable First(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex = 0;
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
public DataTable Last(IList<StudentModel.Student> ListToPage, int RecordsPerPage)
{
PageIndex = ListToPage.Count / RecordsPerPage;
PagedList = SetPaging(ListToPage, RecordsPerPage);
return PagedList;
}
}
}
MainWindow.XAML
<Window x:Class="CodeProjectDemonstration.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CodeProjectDemonstration"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<DataGrid x:Name='dataGrid'
Grid.Column='1'
Grid.Row='1'
Grid.RowSpan='3'
Grid.ColumnSpan='3'
Margin='8' />
<StackPanel Grid.Row='1'
Grid.RowSpan='3'>
<Button Content='Next'
x:Name='NextButton'
Margin='6'
Click='NextButton_Click' />
<Button Content='Previous'
x:Name='PreviousButton'
Margin='6'
Click='PreviousButton_Click' />
<Button Content='First'
x:Name='FirstButton'
Margin='6'
Click='FirstButton_Click' />
<Button Content='Last'
x:Name='LastButton'
Margin='6'
Click='LastButton_Click' />
<StackPanel Orientation='Horizontal'
HorizontalAlignment='Right'>
<Label x:Name='PageInfo' />
<ComboBox x:Name='NumberOfRecords'
SelectionChanged='NumberOfRecords_SelectionChanged' />
</StackPanel>
</StackPanel>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows;
using System.Windows.Controls;
namespace CodeProjectDemonstration
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private int numberOfRecPerPage; //Initialize our Variable, Classes and the List
static Paging PagedTable = new Paging();
static StudentModel StudentList = new StudentModel();
IList<StudentModel.Student> myList = StudentList.GetData();
public MainWindow()
{
InitializeComponent();
PagedTable.PageIndex = 1; //Sets the Initial Index to a default value
int[] RecordsToShow = { 10, 20, 30, 50, 100 }; //This Array can be any number groups
foreach (int RecordGroup in RecordsToShow)
{
NumberOfRecords.Items.Add(RecordGroup); //Fill the ComboBox with the Array
}
NumberOfRecords.SelectedItem = 10; //Initialize the ComboBox
numberOfRecPerPage = Convert.ToInt32(NumberOfRecords.SelectedItem); //Convert the
//Combox Output to type int
DataTable firstTable = PagedTable.SetPaging(myList, numberOfRecPerPage); //Fill a
//DataTable with the First set based on the numberOfRecPerPage
dataGrid.ItemsSource = firstTable.DefaultView; //Fill the dataGrid with the
//DataTable created previously
}
public string PageNumberDisplay()
{
int PagedNumber = numberOfRecPerPage * (PagedTable.PageIndex + 1);
if (PagedNumber > myList.Count)
{
PagedNumber = myList.Count;
}
return "Showing " + PagedNumber + " of " + myList.Count; //This dramatically
//reduced the number of times I had to write this string statement
}
private void NextButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Next(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void PreviousButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Previous(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void FirstButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.First(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void LastButton_Click(object sender, RoutedEventArgs e)
{
dataGrid.ItemsSource = PagedTable.Last(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
private void NumberOfRecords_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
numberOfRecPerPage = Convert.ToInt32(NumberOfRecords.SelectedItem);
dataGrid.ItemsSource = PagedTable.First(myList, numberOfRecPerPage).DefaultView;
PageInfo.Content = PageNumberDisplay();
}
}
}
关注点
所以,在其他示例中我发现重要的东西是控件的耐用性和一些小用户问题。如果我疯狂点击下一步,即使超过最后一条记录,Index
也会继续增加。为了回去,我不得不以同样 +1 的次数疯狂点击上一个按钮。我还发现要向前分页,我必须点击前进按钮两次才能移到下一组记录。我仍然不知道为什么。代码似乎不耐用也不可抽象。我需要将其应用于数十万个返回的项,并且可能跨越许多不同的对象类。我从未能真正使这个系统完全通用,但我知道那里有人可以!
我发现思考上面所有代码只做一件事,而且它都由一个方法驱动,这很有趣。
PagedList = ListToPage.Skip(PageGroup).Take(RecordsPerPage).ToList();
这个单一的 LINQ 查询完成了我们分页所需的一切,如果您直接使用列表而不是 DataTable
,您可以使我的代码小得多。但是,那个小小的 LINQ 查询非常强大,必须用代码层层包裹才能使其用户友好和功能化。它就像法拉利发动机,几乎无用,直到你把它放到一个带有所有漂亮部件的底盘里,突然它就变得如此令人向往。
历史
- 2018-08-26:首次发布