在更新绑定对象之前验证单元格输入

Validating a Cell input before the bound object is updated

我使用 BindingList 将对象绑定到 DataGridView。

我需要在基础绑定对象 属性 更新之前验证键入单元格的数据。

我正在尝试使用 CellValidating 事件,但我已经设置了断点并且绑定对象的 属性 在 CellValidating 之前获得了新值。

我正在寻找类似于 CellBeforeUpdate 的事件,但此网格中不存在。

我是否漏掉了一些明显的东西?

{编辑,这是一个编程错误。我插入了一个将索引移动 1 的列,并且 CellValidating 事件中的代码正在查看不正确的注释。

我认为更好的做法是使用列名而不是索引来避免像这样的错误}

我不同意你的评论……

”the property of the bound object gets the new value before CellValidating does.”

在执行“离开”网格 CellValidating 事件之前,基础数据源不会更改。

CellValidating 事件的开头放置一个断点。然后 运行 代码,对网格中的一个单元格进行更改,然后尝试离开该单元格。当断点命中时,查看底层数据源。它不会显示网格单元格中的更改。

因为你没有显示代码,我只能假设在 CellValidating 事件中,有“某种”检查来验证单元格中的数据是否无效。你如何检查这个是未知的,我们只能假设你正在检查正确的值。

然而,无论如何,如果您检查单元格中的值并确定它是无效值,并希望将单元格值设置为编辑“之前”的原始值,则调用网格 CancelEdit() 方法应该为你做这个。像……

private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) {
  if (e.FormattedValue.ToString() == "some invalid value") {
    dataGridView1.CancelEdit();
  }
}

编辑…

为了提供帮助,我能做的最好的事情就是证明你所说的正在发生......用给定的信息无法重现。

从我能破译的……

  1. 格子用一个BindingList<T>作为一个DataSource格子.

  2. 不知道“T”是什么,只能猜测是基本的class.

  3. 网格已订阅(连接)其 CellValidating 事件。

现在,如果我们尝试重现这个……“基本”Class 可能如下所示,其中一个 int 属性 class 被命名为“ ItemCode”根据您的评论。此外,还添加了一个名为“ItemName”的额外 string 属性。尽可能基本……

public class ItemClass {
  public int ItemCode { get; set; }
  public string ItemName { get; set; }
}

为了提供帮助,我们将创建一个 GetData 方法,该方法 returns 一个 BindingList<ItemClass> 的 15 ItemClass 个对象。我们将使用此绑定列表作为网格的数据源。

private BindingList<ItemClass> GetData() {
  BindingList<ItemClass> bl = new BindingList<ItemClass>();
  for (int i = 1; i < 16; i++) {
    bl.Add(new ItemClass { ItemCode = i, ItemName = "Item " + i });
  }
  return bl;
}

接下来,我们需要连接网格 CellValidating 事件。在此示例中,由于 ItemCode 是一个 int 值,因此 CellValidating 事件将是检查并确保用户在细胞。否则,用户可能会键入字符并抛出网格 DataError. 在此示例中,如果文本不是有效的 int 值,则单元格的值将返回到其在编辑。

这给了我们一个很好的理由来连接这个事件,但它也很方便,所以我们可以在用户“更改”单元格值时检查 BindingList<ItemClass> 列表中的值。

private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) {
  if (e.ColumnIndex == 0) {
    if (!int.TryParse(e.FormattedValue.ToString(), out int value)) {
      dataGridView1.CancelEdit();
    }
  }
}

因此,创建一个新的 winforms 项目并将 DataGridView 拖放到窗体上。除了上面的代码,我们唯一需要的代码是下面…

BindingList<ItemClass> GridBindingList;

public Form2() {
  InitializeComponent();
}

private void Form2_Load(object sender, EventArgs e) {
  GridBindingList = GetData();
  dataGridView1.DataSource = GridBindingList;
}

我们创建了一个“全局”变量 GridBindingList 这样我们就可以在我们设置的任何断点处检查它的值。在表单 Load 事件中,GridBindingList 填充了测试数据,最后将网格 DataSource 设置为此绑定列表。

这是我能破译的与您的代码最接近的匹配项,因为您拒绝显示它。

您声明当用户在网格中的单元格中键入文本然后试图离开该单元格时……GridBindingList 会更新为网格“之前”网格单元格中的文本 CellValidating 事件被调用。

并且我坚持我的评论,即您对此有误。使用上面的代码,你应该可以自己测试一下。

执行时,上面的代码应该会产生类似于左侧表格的内容……

编辑网格中的第二个项目后,它可能看起来像右边的表格……

然后离开单元格,网格CellValidating事件触发。遇到断点,执行在事件的第一行停止,如下所示。

如图所示,GridBindingList的第二项还没有像你说的那样改变。此外,在观察列表的下方,我们可以看到格式化值“fsdgfdgdf”仍然显示在网格中。

您能否提供重现您所描述内容的代码?从你的评论到这个答案…

”I put breakpoints in the property of the underlying object and it 100% fires before the CellValidating event.”

然后“展示”一个这样的例子。我希望这个例子表明底层数据源在网格 CellValidating 事件触发之前不会改变。