65.9K
CodeProject 正在变化。 阅读更多。
Home

带分页的 DataGridView (UserControl)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (13投票s)

2012年8月25日

CPOL

7分钟阅读

viewsIcon

96745

downloadIcon

8509

这是用于 Windows 窗体的带分页的 DataGridView 用户控件。

引言

我创建这个用户控件是为了那些正在寻找 DataGridView 分页功能的人。很多时候,我们在 Windows 应用程序中都会使用带分页功能的 DataGridView。因此,我写这篇文章的目的是展示用户控件的创建过程,并提供带分页功能的 DataGridView。

尽管创建分页功能非常基础,但我想通过这篇文章向您展示用户控件的特性,我认为这将是学习用户控件的最佳主题。人们已经了解 DataGridView 和分页,因此无需额外的知识即可理解其功能。

它会是什么样子?

让我先向您展示这个控件的外观,这样您就知道您将要开发什么了。

创建用户控件: 

我希望您已经知道如何在您的 Windows 窗体上拖放按钮、标签或文本框控件。您的项目中可能已经添加了很多窗体。同样,您可以从“右键单击项目 > 添加 > 新建项”中添加新的 用户控件。或者,如果您想创建可供其他应用程序使用的用户控件,那么您可以添加新的 Windows 窗体控件库 项目。一旦您构建了您的应用程序,它将为您提供 DLL(动态链接库)文件,以便在其他项目中使用。

DataGridView Design
 

如您所见,上面我们有几个按钮控件、一个文本框控件和一个 DataGridView 控件。我们将 DataGridView 的 Dock 属性设置为 Fill,这样即使我们更改用户控件的宽度和高度,它看起来也会很完美。在底部,我们创建了一个 TableLayoutPanel,它将用于正确排列我们的按钮和文本框。TableLayoutPanel 的 Dock 属性设置为 bottom,并带有固定高度。因此,即使调整控件大小,它也不会破坏我们的设计。更改用户控件的外观和感觉后,您现在可以继续进行控件的代码隐藏部分。 

我想这个介绍暂时就足够了。让我们看看 DataGridView 用户控件的代码。 

我们需要在 UserControl 中公开一些属性,以便其他人可以修改它。例如:DataGridView 的宽度、DataGridView 的高度、按钮文本、页面大小、数据源等。 

让我们从查看公开用户控件属性的代码开始。  首先,我们将查看最重要的属性,例如 PageSize。

private int _PateSize = 10;
public int PageSize
{
    get
    {
        return _PateSize;
    }
    set
    {
        _PateSize = value;
    }
} 

PageSize 属性非常重要,没有它我们就无法设置每页要显示多少行。我们将 PageSize 的默认值设置为 10。如果您不设置 PageSize 的值,那么它将每页显示 10 行。 

现在接下来我们将看一下控件的 DataSource 属性。该控件包含我们已绑定到 DataGridView 的 DataTable。尽管这不是一个非常重要的属性,但我们创建它以防用户需要所有数据而不是分页数据。

private DataTable _DataSource;
public DataTable DataSource
{
    get
    {
        return _DataSource;
    }
    set
    {
        _DataSource = value;
    }
} 

其他属性,如 Width 和 Height,对于在运行时设置大小也很重要。虽然用户可以在设计时更改控件的宽度和高度,但我们可以将其添加为属性,以便在运行时进行设置。 

private int _Width;
public int ControlWidth
{
    get
    {
        if (_Width == 0)
            return dataGridView1.Width;
        else
            return _Width;
    }
    set
    {
        _Width = value;
        dataGridView1.Width = _Width;
    }
}
 
private int _Height;
public int ControlHeight
{
    get
    {
        if (_Height == 0)
            return dataGridView1.Height;
        else
            return _Height;
    }
    set
    {
        _Height = value;
        dataGridView1.Height = _Height;
    }
} 

请注意,我为 Height、Width 按钮文本属性添加了一些验证。我们需要此类验证来减少错误消息的可能性。如果用户在设置 Height 值之前尝试获取它,则会得到不正确的结果(值 0)。因此,我们在上述属性中添加了此类验证。

最后,我们将公开按钮文本属性。通常我们熟悉的分页按钮是“第一页”、“上一页”、“下一页”和“最后一页”,但有些用户希望用不同的文本显示该控件,例如大于 (>) 或小于 (<) 符号。这可以通过以下属性设置。尽管以下四个属性不是特别重要,但它们取决于用户的选择。

private string _FirstButtonText = string.Empty;
public string FirstButtonText
{
    get
    {
        if (_FirstButtonText == string.Empty)
            return btnFirst.Text;
        else
            return _FirstButtonText;
    }
    set
    {
        _FirstButtonText = value;
        btnFirst.Text = _FirstButtonText;
    }
}
 
private string _LastButtonText = string.Empty;
public string LastButtonText
{
    get
    {
        if (_LastButtonText == string.Empty)
            return btnLast.Text;
        else
            return _LastButtonText;
    }
    set
    {
        _LastButtonText = value;
        btnLast.Text = _LastButtonText;
    }
}
 
private string _PreviousButtonText = string.Empty;
public string PreviousButtonText
{
    get
    {
        if (_PreviousButtonText == string.Empty)
            return btnPrevious.Text;
        else
            return _PreviousButtonText;
    }
    set
    {
        _PreviousButtonText = value;
        btnPrevious.Text = _PreviousButtonText;
    }
}
 
private string _NextButtonText = string.Empty;
public string NextButtonText
{
    get
    {
        if (_NextButtonText == string.Empty)
            return btnNext.Text;
        else
            return _NextButtonText;
    }
    set
    {
        _NextButtonText = value;
        btnNext.Text = _NextButtonText;
    }
}

现在,让我们看看代码的第二部分,即 DataTable 与 DataGridView 的绑定。在这里,我们需要根据当前页面过滤数据。我们还需要更改显示当前页面的文本框信息。

public void DataBind(DataTable dataTable)
{
    DataSource = dataTable;
    dataGridView1.DataSource = ShowData(1);
} 

当用户将 DataTable 绑定到我们自定义的 DataGridView 时,我们需要向他们显示所有数据中的第一页。因此,我们在 ShowData 私有函数中传递参数 1。上面的函数将内部调用 ShowData 函数,它将动态创建新的 DataTable。

private DataTable ShowData(int pageNumber)
{
    DataTable dt = new DataTable();
    int startIndex = PageSize * (pageNumber - 1);
    var result = DataSource.AsEnumerable().Where((s, k) => (k >= startIndex && k < (startIndex + PageSize)));
 
    foreach (DataColumn colunm in DataSource.Columns)
    {
        dt.Columns.Add(colunm.ColumnName);
    }
 
    foreach (var item in result)
    {
        dt.ImportRow(item);
    }
 
    txtPaging.Text = string.Format("Page {0} Of {1} Pages", pageNumber, (DataSource.Rows.Count / PageSize) + 1);
    return dt;
}  

ShowData 方法包含代码的不同部分。首先,它将 pageNumber 作为参数。假设用户请求第 3 页,那么它将首先检查需要从 DataSource 返回多少条记录。ShowData 函数将返回一个 DataTable,其 DataRow 计数为 PageSize。如果您的最后一页有 5 条记录,那么 DataTable 将只填充 5 条 DataRow。让我们逐一查看上面的代码。

如果你看 ShowData 函数的第 3 行,我们使用了 DataSource,它以 DataTable 的形式包含所有记录。要在 DataTable 上执行 LINQ 查询,首先需要将其转换为 Enumerable 类型。AsEnumerable 将把我们的 DataTable 转换为 Enumerable 类型。然后你可以应用基于索引的 Where 条件。where 条件中的 (s,k) 分别表示 DataRow(s)Index(k)。如果用户请求第 3 页且页面大小为 10,则 startIndex 将为 20。在第 3 页,我们需要显示从 21 到 30 的数据。但是 DataRows 是基于索引存储的,因此我们需要获取从 2029 的记录(包括第 20 行和第 29 行)。因此 where 子句表示返回从 20 到 (20+10=30) 的记录。但是,在右侧操作中,我们使用了 (<) 大于符号,而不是 (<=) 大于或等于。因此不会返回第 30 条记录。

然后,接下来的 foreach 循环将在 DataGridView 中创建 DataColumns。如果您不在 DataGridView 中创建数据列,那么它将无法正确显示数据。

在最后一个 foreach 循环中,我们将所需的行导入到新的 DataTable,即 dt。我们不能直接添加项目,如 dt.Rows.Add(item),因为该数据行已经与另一个 DataTable 关联。因此,我们在这里使用了 dt.ImportRow(item)

当用户点击“首页”按钮时,我们需要显示从 第 0 个索引到 PageSize 的第一页。如您在 btnFirst_Click 事件中看到的,我们添加了对 _CurrentPage 的检查。如果用户已经在第一页,点击“首页”按钮没有任何意义。因此,我们添加了一个 MessageBox 来显示信息。如果 _CurrentPage 不是第一页,那么我们将其设置为第一页,然后内部调用 ShowData 函数并绑定 DataGridView 的 DataSource。

private void btnFirst_Click(object sender, System.EventArgs e)
{
    if (_CurrentPage == 1)
    {
        MessageBox.Show("You are already on First Page.");
    }
    else
    {
        _CurrentPage = 1;
        dataGridView1.DataSource = ShowData(_CurrentPage);
    }
} 

“下一页”按钮将把 DataGridView 数据导航到下一页。因此,DataGridView 会相应地更新其数据。当用户单击“下一页”按钮时,将计算最后一页,因为如果用户已经在最后一页,我们应该提示消息,告知请求的页面不存在,或者用户已经在最后一页,无法导航到下一页。如果请求的页面不是最后一页,则增加 CurrentPage 并调用 ShowData 函数。

private void btnNext_Click(object sender, System.EventArgs e)
{
    int lastPage = (DataSource.Rows.Count / PageSize) + 1;
    if (_CurrentPage == lastPage)
    {
        MessageBox.Show("You are already on Last page, you can not go to next page of Last page.");
    }
    else
    {
        _CurrentPage += 1;
        dataGridView1.DataSource = ShowData(_CurrentPage);
    }            
} 

“上一页”按钮将使 DataGridView 导航到上一页。这意味着如果您在第 3 页,点击“上一页”按钮将显示第 2 页的数据。我们还需要确保如果用户已经在第一页,则第一页没有上一页。如果请求的页面不是第一页,则递减 _currentPage 值并调用 ShowData 函数。

private void btnPrevious_Click(object sender, System.EventArgs e)
{
    if (_CurrentPage == 1)
    {
        MessageBox.Show("You are already on First page, you can not go to previous of First page.");
    }
    else
    {
        _CurrentPage -= 1;
        dataGridView1.DataSource = ShowData(_CurrentPage);
    }
} 

“最后一页”按钮点击事件将把网格数据导航到最后一页。为了检查用户上次请求的页面是否已经是最后一页,我们必须将其存储在某个变量中。我们创建了一个局部变量 previousPage 来存储之前请求的页面。如果前一页和最后一页索引相同,则应提示一条消息,说明用户已经在最后一页。如果用户请求的不是最后一页,则只需将最后一页索引传递给 ShowData 函数。

private void btnLast_Click(object sender, System.EventArgs e)
{
    int previousPage = _CurrentPage;
    _CurrentPage = (DataSource.Rows.Count / PageSize) + 1;
 
    if (previousPage == _CurrentPage)
    {
        MessageBox.Show("You are already on Last Page.");
    }
    else
    {
        dataGridView1.DataSource = ShowData(_CurrentPage);
    }
}   

如您所见,在我们所有的按钮点击事件中,都有一些信息消息。如果您想自定义这些消息,那么您还需要为信息消息创建属性。我将此练习留给初学者。

希望您从以上代码中学到了一些东西,欢迎您的建议。如果您觉得这个控件缺少什么,请发表您的留言。

历史   

2012年8月24日:首次发布

© . All rights reserved.