【问题标题】:Can I use multiple DataGridViewRows in a single form?我可以在一个表单中使用多个 DataGridViewRows 吗?
【发布时间】:2025-12-07 20:35:01
【问题描述】:

我正在设计一个带有包含 2 个选项卡的 TabControl 的 Windows 窗体。我在每个选项卡中都有一个 DataGridView,两者都填充了相同的列(包括 DataGridViewCheckBoxColumn),但参数不同,因此最终用户更容易使用它们,而不是全部包含在 1 个网格中。

我以为我可以为每个 DatagridView 设置一个 DataGridViewRow,并且它只会在调用时将它们用作新实例,但事实并非如此。

当用户选中或取消选中第一个选项卡的 DataGridView 中的复选框时,以下代码可以正常工作;

private void dgvChq_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
        int column = e.ColumnIndex;
        int row = e.RowIndex;
        if (column == 5)
        {
            DataGridViewCheckBoxCell c = dgvChq[e.ColumnIndex, e.RowIndex] as DataGridViewCheckBoxCell;
            if (c != null)
            {
                string a = e.FormattedValue.ToString();
                if (a == "True")
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = @approved WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@approved", DateTime.Today);
                        cmd.Parameters.AddWithValue("@ordNo",dgvChq.Rows[row].Cells[0].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dr = dgvChq.SelectedRows[0];
                    dr.Cells[4].Value = DateTime.Today.ToString();
                }
                else
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = NULL WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@ordNo", dgvChq.Rows[row].Cells[0].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dr = dgvChq.SelectedRows[0];
                    dr.Cells[4].Value = "";
                }
            }
        }
    }

所以我想我可以使用不同的 DataGridViewRow 并将此代码重新用于另一个选项卡中的另一个 DataGridView,但它失败了;

private void dgvCredit_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
        int column = e.ColumnIndex;
        int row = e.RowIndex;
        if (column == 0)
        {
            DataGridViewCheckBoxCell c = dgvCredit[e.ColumnIndex, e.RowIndex] as DataGridViewCheckBoxCell;
            if (c != null)
            {
                string a = e.FormattedValue.ToString();
                if (a == "True")
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = @approved WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@approved", DateTime.Today);
                        cmd.Parameters.AddWithValue("@ordNo", dgvCredit.Rows[row].Cells[1].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dgvr = dgvCredit.SelectedRows[0];
                    dgvr.Cells[4].Value = DateTime.Today.ToString();
                }
                else
                {
                    using (SqlCommand cmd = con.CreateCommand())
                    {
                        cmd.CommandText = "UPDATE Customer.OrderHeader SET DateApproved = NULL WHERE OrderNumber = @ordNo";

                        cmd.Parameters.AddWithValue("@ordNo", dgvCredit.Rows[row].Cells[1].Value.ToString());

                        con.Open();
                        cmd.ExecuteNonQuery();
                        con.Close();
                    }
                    DataGridViewRow dgvr = dgvCredit.SelectedRows[0];
                    dgvr.Cells[4].Value = "";
                }
            }
        }
    }

我在这里遗漏了什么,还是不能在同一个表单中使用多个 DataGridViewRows?

也许有一种更“动态”的方式我也可以这样做,这可能有助于解决我的问题?

PS:似乎第二个datagridviewrow(dgvr)在单击复选框时仍然显示为null,所以这是发生错误的地方。

【问题讨论】:

  • 如果您复制并粘贴了数据网格,您是否有可能做到让它们都具有相同的事件处理程序?
  • 你的代码急需重构和分离业务逻辑:( :(
  • 而不是使用两个数据网格,是否可以根据文本框的文本简单地过滤一个?
  • 可以使用多个datagridviewrows,但措辞不正确。此外,您现在面临的问题是什么?
  • @nawfal 在第二个数据网格中单击复选框时出现错误。我更新了我的问题以强调这一点。

标签: c# .net winforms datagridview


【解决方案1】:

@Keefa2011,问题不在于 DataGridViewRow 实例。同意它们是引用类型,因此在创建此类实例成员变量时应该小心。但在您的代码中,它们的范围仅限于特定事件。

问题在于你提到的那一行。除非启用了FullRowSelect,否则您总是会在DataGridViewRow dgvr = dgvCredit.SelectedRows[0]; 处遇到异常,因为'因为选定的行数将始终为零。你需要处理它。您可以通过启用全行选择或更改该错误行(如下所示)来实现。我不确定为什么您只收到第二个 dgv 的错误。也许第一个 dgv 启用了全行选择?

此外,您的代码急需重构。你用两页写的其实就是这么多:

private void dgvChq_CellValidating(object sender, 
                                   DataGridViewCellValidatingEventArgs e)
{
    if (e.ColumnIndex != 5)
        return;

    HandleCheckedChanged(dgvChq, e, 
                         Convert.ToInt32(dgvChq.Rows[e.RowIndex].Cells[0].Value));
}

private void dgvCredit_CellValidating(object sender, 
                                      DataGridViewCellValidatingEventArgs e)
{
    if (e.ColumnIndex != 0)
        return;

    HandleCheckedChanged(dgvCredit, e, 
                         Convert.ToInt32(dgvCredit.Rows[e.RowIndex].Cells[1].Value));
}

private void HandleCheckedChanged(DataGridView dgv, 
                                  DataGridViewCellValidatingEventArgs e, int id)
{
    object toBeDisplayedDateValue = (bool)e.FormattedValue ? (DateTime?)DateTime.Today : null;

    using (SqlCommand cmd = con.CreateCommand())
    {
        cmd.CommandText = @"UPDATE Customer.OrderHeader 
                            SET    DateApproved = @approvedDate 
                            WHERE  OrderNumber = @ordNo";

        cmd.Parameters.AddWithValue("@approvedDate", toBeDisplayedDateValue);
        cmd.Parameters.AddWithValue("@ordNo", id);

        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }

    dgv.Rows[e.RowIndex].Cells[4].Value = toBeDisplayedDateValue; //you could just do 
                                                                  //this much 
    //or

    //DataGridViewRow dgvr = dgv.SelectedRows[0]; //this line works only if there 
                                                  //is at least one selected row when 
                                                  //validating cell. For this you 
                                                  //require 
                                                  //dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect
}

您实际上应该将数据库操作移至另一个类以获得更简洁的设计。您的代码最大的警告是您将 id 作为"@ordNo" 传递。我只将它移到函数之外,因为我不确定您是否应该从 UI 端获取该值。该逻辑应该从数据库端处理。换句话说,您应该将订单号保留在其他地方,可能作为 dgv 行的标签左右。如果您确定 gridview 单元格中的值实际上可以占数据库中的正确记录,那么继续,您只需将订单号列的列索引作为参数传递。

另一件事,我认为您实际上不需要单元格验证事件,因为每次您将焦点移出单元格时都会触发它。由于它的 db 操作可能很昂贵,因此您可以注册 cell value changed event,它仅在单元格的值更改时才会执行操作。这里要注意的一件事是,您应该仅在完全加载所有单元格后注册事件。否则,即使最初填充记录,它也会被触发。

例如,

private void Form1_Load(object sender, EventArgs e)
{
    //---------------------------------------------
    // load dgv...
    //---------------------------------------------

    dgvChq.CellValueChanged += dgvChq_CellValueChanged;
    dgvCredit.CellValueChanged += dgvCredit_CellValueChanged;
}

private void dgvChq_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex != 5)
        return;

    HandleCheckedChanged(dgvChq, e, 0);
}

private void dgvCredit_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (e.ColumnIndex != 0)
        return;

    HandleCheckedChanged(dgvCredit, e, 1);
}

private void HandleCheckedChanged(DataGridView dgv, DataGridViewCellEventArgs e, 
                                  int columnIndexOfOrderNo)
{
    DataGridViewCheckBoxCell c = dgv[e.ColumnIndex, e.RowIndex] as DataGridViewCheckBoxCell;
    object toBeDisplayedDateValue = (bool)c.EditedFormattedValue ? (DateTime?)DateTime.Today : null;

    using (SqlCommand cmd = con.CreateCommand())
    {
        cmd.CommandText = @"UPDATE Customer.OrderHeader 
                            SET    DateApproved = @approvedDate 
                            WHERE  OrderNumber = @ordNo";

        cmd.Parameters.AddWithValue("@approvedDate", toBeDisplayedDateValue);
        cmd.Parameters.AddWithValue("@ordNo", 
                                    Convert.ToInt32(dgv.Rows[e.RowIndex].Cells[columnIndexOfOrderNo].Value));

        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }

    dgv.Rows[e.RowIndex].Cells[4].Value = toBeDisplayedDateValue;
}

理想情况下,您应该将要更新的字段的 columnindex(在您的情况 4 中)传递给函数,因为这些东西在未来很容易对 dgvs 有所不同。

【讨论】:

  • 谢谢你 Nawfal,我已经使用你的压缩、重构代码来解决我的问题,它做得很好。周末我开始玩 Cell Value Changed 活动,但无法在此处获得您的结果,因此也感谢您:)
最近更新 更多