Datagridview rowsadded 事件在 bindsource 完成之前触发

Datagridview rowsadded event fires before bindsource completed

首先我有:

我得到了一个 datagridview 和 bindsource(目前我自己的数据表来自 sql-call)。绑定源有多个列,但其中一个是我的数据的特定“位置”列。

我想做的事情:

当我将绑定源设置为 datagridview 时,我想更改具有指定“位置”的行的颜色。

我做了什么:

我认为创建一个 RowsAdded 事件并检查添加行的“位置”列是否等于我指定的位置就足够了。基于此,我会更改行的背景色。

代码是这样的:

private void setBindSource(BindingSource bindSource)
{
   gridview.DataSource = bindSource;
   gridview.EndEdit();
   bindSource.EndEdit();
}

private void gridView_RowsAdded(object sender, System.Windows.Forms.DataGridViewRowsAddedEventArgs e)
{
   if (e.RowIndex == -1 || e.RowCount == 0)
   {
      return;
   }

   for (int index = e.RowIndex; index <= e.RowIndex + e.RowCount - 1; index++)
   {
      DataGridView dgv = sender as DataGridView;
      DataGridViewRow row = dgv.Rows[index];
   
      if ((Int32)row.Cells["position"].Value == specificPosition)
         row.DefaultCellStyle.BackColor = Color.Green;
   }
}

我面临的问题:

rowsadded 事件按预期触发(在 gridview.DataSource = bindSource;)。但是该行本身没有任何数据(使用的数据表显示了预期的数据)。所以它显然会抛出错误,它找不到指定的列“位置”。我假设 gridview 还没有完全初始化?

添加行后如何更改行的颜色?

当行的背景颜色取决于特定的单元格值时,在“更改”行颜色时应牢记一些事项。

测试特定单元格值和更改行颜色的代码所在的“位置”可以在许多不同的网格事件中完成,并且所有事件(基本上)都会完成相同的事情。然而,做一些简单的测试来帮助您确定您选择的“网格事件”是否“真的”是您需要或应该使用的可能是有益的。

例如,按照建议,您可以订阅网格 CellFormatting 事件来执行您描述的操作,它将按预期工作。但是,使用此事件会产生一个微妙但可能存在的问题。对于初学者来说,几乎所有的网格“格式化”事件都会触发很多次。

如果用户只是将光标移到网格上,则将触发格式化事件。因此,如果光标正好移到目标单元格上,就会执行代码。这可能是可以接受的;但是,从这里可以清楚地看出,检查单元格的值确实是不必要的,因为单元格的值没有改变。用户只需将光标移动到目标单元格上。

如前所述,这可能是可以接受的,但是,如果这种不必要的事件调用混合在一起,不难看出这种情况的许多组合会影响 UI 的性能并且用户可能会遇到缓慢的 UI.

这里的主要思想是……不要将您的代码放在一个会被不必要地触发的事件中,这是一个很好的例子。它可能有效,但代码正在“创建”额外和不必要的执行步骤。


鉴于此,您尝试连接网格 RowsAdded 事件可能会奏效。但是,正如您所指出的,当数据“最初”加载时以及用户手动添加新行或代码添加新行时,似乎需要做一些不同的事情。当数据“初始”加载时,您认为某些单元格可能尚未初始化是正确的。至少你的代码正在查看的单元格。

这可以解决,但是,使用 RowsAdded 事件会遗漏另一个问题……

_“what if the user CHANGES a cells value of a ‘’position’’ cell?”

在这种情况下不会触发新行事件。如果用户将“位置”单元格更改为 specifiedPosition 的值,则该行的背景颜色将不会更改。

考虑到所有这些,为了提供帮助,最好将我们想要代码的“时间”指向 运行。据我所知,这将是“当”位置列中的单元格中的值发生变化时。因此,我建议您订阅 grids CellValueChanged 活动。如果“更改”的单元格是“位置”单元格,则测试单元格值并相应地为行着色。

这将最大限度地减少对代码的不必要调用次数,并将解决前面描述的用户“更改”单元格值时出现的问题。当“用户”添加新行时,这也将起作用。不幸的是,当最初加载数据或通过代码添加新行时,不会触发此事件。

鉴于此,除了在代码中为新添加的行设置正确的行颜色之外,还有一种可能的解决方案是在网格最初加载数据后为行着色,一些小方法可能会派上用场。

第一种方法只是根据“位置”单元格的值为给定的一 (1) 行着色。这假设给定的行不是 null 并且位置列存在并且它是有效的 int.

private void ColorRow(DataGridViewRow row) {
  if ((int)row.Cells["position"].Value == specificPosition) {
    row.DefaultCellStyle.BackColor = Color.LightGreen;
  }
  else {
    row.DefaultCellStyle.BackColor = Color.White;
  }
}

我们可以在网格 CellValueChanged 事件中使用 ColorRow 方法来为特定行着色。此外,在表单加载事件中,在数据初始设置到网格中之后,我们可以调用一个附加方法,该方法简单地循环遍历网格的所有行并调用上面的 ColorRow 方法。这个 ColorSpecificRows 方法可能看起来像……

private void ColorSpecificRows() {
  foreach (DataGridViewRow row in gridView.Rows) {
    if (!row.IsNewRow) {
      ColorRow(row);
    }
  }
}

接下来,剩下的就是订阅网格 CellValueChanged 事件,它可能看起来像……

private void gridView_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
  ColorRow(gridView.Rows[e.RowIndex]);
}

为了帮助测试这个,下面是一个完整的例子。创建一个新的 win 表单解决方案,在表单中添加一个 DataGridView 和一个 Button。该按钮用于演示以编程方式添加行。

DataTable GridTable;
BindingSource GridBS;
Random rand = new Random();
int specificPosition = 2;
int formattingCount = 0;

public Form1() {
  InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e) {
  GridTable = GetTable();
  FillTable(GridTable);
  GridBS = new BindingSource(GridTable, null);
  gridView.DataSource = GridBS;
  ColorSpecificRows();
}

private DataTable GetTable() {
  DataTable dt = new DataTable();
  dt.Columns.Add("Col0", typeof(string));
  dt.Columns.Add("position", typeof(int));
  dt.Columns.Add("Col2", typeof(string));
  return dt;
}

private void FillTable(DataTable dt) {
  for (int i = 0; i < 10; i++) {
    dt.Rows.Add("C0R" + i, rand.Next(1, 4), "C2R" + i);
  }
}

private void button1_Click(object sender, EventArgs e) {
  GridTable.Rows.Add("new0", 2, "new0");
  GridTable.Rows.Add("new1", 3, "new0");
  GridTable.Rows.Add("new2", 2, "new0");
  ColorSpecificRows();
}

最后,出于测试目的,您可以连接网格 CellFormatting 事件以演示调用该偶数的频率。

private void gridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
  Debug.WriteLine("CellFormatting - Enter -- FormattingCount: " + ++formattingCount);
   // code to change row color
  Debug.WriteLine("CellFormatting - Leave");
}

我希望这是有道理的。