【问题标题】:Add row number column to DataGridView bound to DataTable将行号列添加到绑定到 DataTable 的 DataGridView
【发布时间】:2020-03-13 06:41:33
【问题描述】:

在 ADO.NET 中,我使用 DataAdapter.Fill (..) 调用来使用数据库中的值填充 DataTable。然后我将 DataTable 绑定到 DataGrid 以允许滚动浏览所有值、编辑并将更改更新回数据库。所有标准的东西。我对所有这些都使用 Visual Studio Windows 窗体向导,因为它非常标准。

但现在我想显示一个左侧列(不是数据库行的一部分)来对 DataGrid 中显示的行进行编号。我仍然希望将绑定的行数据保持原样,并自动更新回数据库等等。显然,我不能用向导来做到这一点。

方法一

如果我手动执行此操作,我会更改 DataTable 加载查询以包含一个虚拟整数列,以将所需的行号列注入返回的表中:

SELECT ‘’ as Num, * from MyTable

然后我会在将 DataTable 绑定到网格之前以编程方式将行号插入该字段,这将根据需要显示行号。我希望 DataTable 的自动更新代码会忽略网格中的额外列。

方法2

另一种可能的方法是在绑定到 DataSource(我的 DataTable)后以编程方式将新列添加到 DataGrid。然后我会在显示网格之前用行号填充新列。

问题

但在我放弃方便的向导并为所有事情做手动工作之前,我想问一下是否有一种标准的方法来做这种事情。我不敢相信我是第一个想要在网格显示中使用行号(不是数据库行的一部分)的人。

我已经在这里和其他各种论坛上搜索过想法,但没有人谈论当您将新列注入表加载查询(方法 1)或绑定 DataGrid 后的网格中时更新代码会发生什么到数据源(方法 2)。

我什至想过使用从两个不同绑定源馈送的两个相邻网格控件。但是在滚动期间保持它们同步所需的代码似乎需要更多的工作。

谁能指出解决此问题的最佳方法或提供代码 sn-p 吗?我可以进入表单设计器生成的代码以将列添加到绑定的 DataGrid,但我在尝试查找和理解将更改更新回数据库的更新部分时迷失了方向。谢谢。

【问题讨论】:

  • 您可能可以根据实际数据库(您没有说)使用 SQL 将行号添加到数据库查询结果中。如果您可以在数据表中有未绑定的列。您可以添加这样的列并将结果填充到行绘制事件中。这样,您就不会为保持两个不同的事物同步而头疼。请注意,如果是WinForms,则几乎可以肯定是DataGridView 而不是DataGrid。使用关键字Datagridview row number c# 仅在该网站上就有 61,000 次点击
  • 感谢您的帮助。抱歉,我没有说我正在使用 MS Access 和 Windows DataGridViews 以及 Infragistics UltraGrid(但我的问题更笼统)。是的,我痛苦地意识到各种搜索表达式在互联网上的大量点击。我确定我已经调查了 100 多个。是的,我知道我可以将未绑定的列添加到网格中,但对我来说,关键问题是这样做是否会仅影响绑定列上的数据库更新。
  • 我很确定 MS Access 不支持行号,但这并不妨碍您通过 SQL SELECT 语句添加列并更新 PrePaint 事件中的值。基本的 DB Provider 对象当然足够聪明,即使您手动添加它也不会被人工列混淆。您所要做的就是尝试看看“巫师”是否犹豫
  • 感谢最近的海报!你们中的一个人断言,数据更新对象不会被不属于基础 DataTable 的其他列混淆。你们中的一个人指出,我可以通过设置 HeaderCell.Value 文本在网格本身中显示行号。我的问题的两个很好的答案。

标签: c# winforms datagridview datatable row-number


【解决方案1】:

有一些不错的选择,它们不会干扰数据的查询或结构,并且仅基于 GUI 逻辑:

  • 使用RowPostPaint事件在RowHeader上绘制行号
  • 使用RowPrePaint事件将行号分配给行的HeaderCell
  • 创建一个新的DataGridViewRowNumberColumn 以显示行号

使用 RowPostPaint 事件在 RowHeader 上绘制行号

您可以处理RowPostPaint 并在标题单元格中绘制行号。以下代码显示了一个简单的逻辑:

private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
    var g = (DataGridView)sender;
    var r = new Rectangle(e.RowBounds.Left, e.RowBounds.Top,
        g.RowHeadersWidth, e.RowBounds.Height);
    TextRenderer.DrawText(e.Graphics, $"{e.RowIndex + 1}",
        g.RowHeadersDefaultCellStyle.Font, r, g.RowHeadersDefaultCellStyle.ForeColor);
}

上面的代码已经足够好了,但是您可能希望通过将 datagridview 单元格对齐映射到 this 方法等文本格式标志来增强逻辑。您可能还希望将逻辑放在自定义 DataGridView 中并覆盖 OnRowPostPaint 并在派生的 DataGridViewtrue 时设置 DoubleBuffered 属性。

使用 RowPrePaint 事件将行号分配给行的HeaderCell 如果将字符串值分配给标题单元格的Value 属性,它将显示在行标题上。

您在循环中分配值,在这种情况下,您需要处理 RowAddedRowRemoved 事件并重新分配行号。更好的解决方案是使用RowPrePaint 并检查标题单元格的值是否不正确,然后更正它:

private void dataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
    var g = (DataGridView)sender;
    if (e.RowIndex > -1 && $"{g.Rows[e.RowIndex].HeaderCell.Value}" != $"{e.RowIndex + 1}")
    {
        g.Rows[e.RowIndex].HeaderCell.Value = $"{e.RowIndex + 1}";
    }
}

创建一个新的DataGridViewRowNumberColumn 以显示行号

第二个选项是创建一个新的可重用列类型来显示行号。您可以像使用任何其他列类型一样使用此列类型。您可以在设计时或运行时添加列。

using System.ComponentModel;
using System.Windows.Forms;
public class DataGridViewRowNumberColumn : DataGridViewColumn
{
    public DataGridViewRowNumberColumn() : base()
    {
        this.CellTemplate = new DataGridViewRowNumberCell();
        this.Width = 40;
        this.SortMode = DataGridViewColumnSortMode.NotSortable;
    }
    [Browsable(false)]
    [DefaultValue(true)]
    public override bool ReadOnly
    {
        get { return true; }
        set { base.ReadOnly = true; }
    }
}
public class DataGridViewRowNumberCell : DataGridViewTextBoxCell
{
    protected override void Paint(System.Drawing.Graphics graphics,
        System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle cellBounds,
        int rowIndex, DataGridViewElementStates cellState, object value,
        object formattedValue, string errorText, DataGridViewCellStyle cellStyle,
        DataGridViewAdvancedBorderStyle advancedBorderStyle,
        DataGridViewPaintParts paintParts)
    {
        base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value,
            formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
    }
    protected override object GetValue(int rowIndex)
    {
        return rowIndex + 1;
    }
    protected override bool SetValue(int rowIndex, object value)
    {
        return base.SetValue(rowIndex, rowIndex + 1);
    }
}

【讨论】:

  • 哇,多么棒的答案! (还有代码 sn-ps。)你显然是这方面的大师。谢谢! - 凯文
【解决方案2】:

见:DataGridViewRow.HeaderCell Property – TnTinMn

TnTinMn 提供了最简单的答案,可以实现我的目标。我在这里复制了他的评论,以便将其标记为已回答的问题。

加载网格后,只需沿着行向下走并将行标签(行号)分配给行左端的“标题单元格”。好的部分是行号是网格中的一个标签,而不是网格中的一列未绑定数据。

【讨论】:

  • 如果用户可以添加或删除行,那么您需要处理添加行和删除行事件并将新的行号分配给标题单元格。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-20
  • 1970-01-01
  • 1970-01-01
  • 2020-10-30
  • 2011-02-13
相关资源
最近更新 更多