我正在设计一个带有TabControl的窗体,其中包含2个选项卡。我在每个选项卡中都有一个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,因此这就是发生错误的位置。
答案 0 :(得分: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端获得该值。应该从db端处理该逻辑。换句话说,您应该将订单号保留在其他位置,可能作为dgv行的标记左右。如果您确定gridview单元格中的值实际上可以计入数据库中的正确记录,那么请继续,您只需要传递order number列的列索引作为参数。
另一件事,我不认为你实际上需要细胞验证事件,因为每当你将焦点移出细胞时它就会被激活。由于它的数据库操作很昂贵,你可以注册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的这些内容可能会有所不同。