数据集中的数据验证可以通过多种方式来完成:
(1)通过主键与唯一条件约束来进行唯一性验证;
(2)通过外键约束来确保数据引用完整性;
(3)使用字段的相关属性,如AllowDBNull、MaxLength与Unique来辅助完成数据验证操作;
(4)自行替应用程序书写数据验证代码,以便在字段和记录变更事件期间检查数据。
前提:在DataGridView控件中修改字段(不包括新行)。
在默认状态下,每次变更一个字段时将会顺序引发4个事件:首先会引发被更改字段的ColumnChanging与ColumnChanged事件,接着是RowChanging与RowChanged事件。值得注意的是,当修改完一个字段后,焦点离开该字段,但仍在该字段所在行时,仅引发ColumnChanging与ColumnChanged事件,此时可继续修改该行的其它字段,并引发相应事件。一旦焦点离开该行,若焦点离开之前刚修改完一个字段值,不管焦点是否仍落在该列,都将顺序引发4个事件;否则引发被修改行的RowChanging与RowChanged事件。
Row.BeginEdit()会将记录置于编辑模式。在此模式中,事件将暂时停止以便在不触发字段约束的情况下,对一个记录的多个字段进行修改。而等到调用Row.EndEdit()时,又会触发约束。
当用户修改数据绑定控件的数据时(如DataGridView控件),会隐式调用被修改行的BeginEdit(),当焦点离开该行时又会隐式调用该行的EndEdit(),从而触发字段约束。(如AllowDBNull、Unique约束等)
字段值改变会调用4个改变事件,即ColumnChanging、ColumnChanged、RowChanging与RowChanged事件,这时有一个问题,如果自行书写数据验证代码,又该写在的哪个事件中呢?
我写了一段试验代码,对不同的执行结果进行了分析。
private void DataTableTest4_Load(object sender, EventArgs e)
{
string sql = "select * from dbo.客户";
tbl = Rabbit.DBUtility.DBHelperSql.Query(sql).Tables[0];
tbl.Columns[0].AllowDBNull = false;
dataGridView1.DataSource = tbl;
tbl.Columns["公司名称"].AllowDBNull = false;
tbl.Columns["公司名称"].DefaultValue = "新世纪公司";
tbl.ColumnChanging += new DataColumnChangeEventHandler(tbl_ColumnChanging);
tbl.ColumnChanged += new DataColumnChangeEventHandler(tbl_ColumnChanged);
tbl.RowChanging += new DataRowChangeEventHandler(tbl_RowChanging);
tbl.RowChanged += new DataRowChangeEventHandler(tbl_RowChanged);
}
private void tbl_ColumnChanging(object sender, DataColumnChangeEventArgs e)
{
//e.Row.EndEdit();
//e.Row.CancelEdit();
Console.WriteLine(e.Row.RowState.ToString());
}
private void tbl_ColumnChanged(object sender, DataColumnChangeEventArgs e)
{
//e.Row.CancelEdit();
//e.Row.EndEdit();
Console.WriteLine(e.Row.RowState.ToString());
}
private void tbl_RowChanging(object sender, DataRowChangeEventArgs e)
{
//e.Row.CancelEdit();
//e.Row.EndEdit();
Console.WriteLine(e.Row.RowState.ToString());
}
private void tbl_RowChanged(object sender, DataRowChangeEventArgs e)
{
//e.Row.EndEdit();
//e.Row.CancelEdit();
Console.WriteLine(e.Row.RowState.ToString());
}
操作:修改DataGridView控件某行(该行不是新行)某列的值,然后焦点离开该行。
结果:见下面8个表
表格说明:第1行表示事件,第2行表示在对应事件中调用行的哪个方法,第3行表示事件的执行顺序,第4行表示每次执行对应事件时行的状态,第5行表示执行结果。
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
EndEdit() |
|
|
|
1/4 |
5 |
2/6 |
3/7 |
Unchanged/ Modified |
Modified |
Unchanged/ Modified |
Modified/Modified |
修改成功 |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
|
EndEdit() |
|
|
1 |
2/5 |
3 |
4 |
Unchanged |
Unchanged/ Modified |
Unchanged |
Modified |
修改成功 |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
|
|
EndEdit() |
|
1 |
2 |
出现异常 |
|
Unchanged |
Unchanged |
|
|
不能在RowChanging中调用EndEdit(),同时也不能调用CancelEdit() |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
|
|
|
EndEdit() |
1 |
2 |
3 |
4 |
Unchanged |
Unchanged |
Unchanged |
Modified |
修改成功 |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
|
|
|
|
1 |
2 |
3 |
4 |
Unchanged |
Unchanged |
Unchanged |
Modified |
修改成功 |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
CancelEdit() |
|
|
|
1 |
2 |
3 |
4 |
Unchanged |
Unchanged |
Unchanged |
Modified |
修改成功 |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
|
CancelEdit() |
|
|
1 |
2 |
|
|
Unchanged |
Unchanged |
|
|
未修改 |
ColumnChanging |
ColumnChanged |
RowChanging |
RowChanged |
|
|
|
CancelEdit() |
1 |
2 |
3 |
4 |
Unchanged |
Unchanged |
Unchanged |
Modified |
修改成功 |
结论1:分析上面8个表格的执行结果,可以成功编辑的有6种方案,可以取消编辑的只有1种方案。显而易见,Row.EndEdit()和Row.CancelEdit()在调用条件上是对立的,而这两个方法最好是在一个条件语句的两个对立逻辑下被分别调用。因此,为了满足这一条件,数据验证代码应该写在ColumnChanged事件中;
结论2:在记录修改成功的6个方案中,该行状态变为Modified都是在调用RowChanged事件时(该事件之后保持Modified是必然的),在此之前,在其它3个事件中行状态都不会由Unchanged变为成功修改后的Modified。