2007-08-25
使用DGV和DataSet的零散笔记
都是我最近碰到的一些编程细节,有些内容有主观推断的成分,不一定正确。
BindingSource会自动临时挂起约束?
如果DGV直接以数据表(DataTable)为数据源,编辑行时可能会很麻烦:你刚编辑完一个单元格,还没有离开当前行,只是移到当前行的另一单元格,就会得到一个DataError。使用BindingSource作为DGV和DataTable的中间层可以解决这个问题,它能保证只要没有离开当前行,就不启用约束检查。
编辑DGV的行时,如果数据完整性被破坏,如何撤消事务?
在DGV的DataError事件中只能取消当前单元格的编辑,不能在行级别上取消编辑。所以这里存在一个潜在的问题:连续编辑了一行中的多个单元格后想放弃修改怎么办?我现在知道的一个方案是使用DataTable的RejectChanges方法,DataTable通过AcceptChanges和RejectChanges方法实现对事务的支持。这个方案要求在DGV的RowValidated事件中调用DataTable的AcceptChanges方法。
DGV在显示之前不会加载数据?
新创建的DGV在设置好数据源后不会立即加载数据——即它的列集合和行集合仍为空,当它显示(由于被添加到某个Control.Controls 中或被设置了Parent 属性) 时,才可以通过它的Rows 属性访问数据。这个特性似乎是在强调:DGV是个不折不扣的UI,在内存中使用不是它的初衷。
如何向包含自动递增列的数据表中添加数据?
可以使用 DataRow 类的 ItemArray 属性并传入值的数组,来创建新行。对于其 AutoIncrement 设置为 true 的列,这是一个潜在的问题,因为其值是自动生成的。若要使用 ItemArray 属性,请将空引用(在 Visual Basic 中为 Nothing)放入数组中的列的位置。在 ItemArray 中传递空引用(在 Visual Basic 中为 Nothing) 指示未指定任何值。
假设Data为DataSet,其中名为TABLE_GROUPS的数据表包含名称分别为COLUMN_GROUP_ID和COLUMN_GROUP_NAME的两列,COLUMN_GROUP_ID列为自动递增列。
下面的代码是正确的:
Data.Tables[TABLE_GROUPS].Rows.Add(null, DBNull.Value);
下面的代码会导致ArgumentException:不能将列“GroupId”设置为空。请改用DBNull。
Data.Tables[TABLE_GROUPS].Rows.Add(DBNull.Value, DBNull.Value);
下面的代码会失败,语句1导致ArgumentException:不能将列“GroupId”设置为空。请改用DBNull。
DataRow newRow = Data.Tables[TABLE_GROUPS].NewRow();
newRow[COLUMN_GROUP_ID] = null; // 语句1
newRow[COLUMN_GROUP_NAME] = "group1";
Data.Tables[TABLE_GROUPS].Rows.Add(newRow);
下面的代码也会失败,语句2语句1导致ArgumentException:不能将列“GroupId”设置为空。请改用DBNull。
DataRow newRow = Data.Tables[TABLE_GROUPS].NewRow();
newRow[COLUMN_GROUP_ID] = DBNull.Value; // 语句2
newRow[COLUMN_GROUP_NAME] = "group1";
Data.Tables[TABLE_GROUPS].Rows.Add(newRow);
P.S. 从这里可以看出null和DBNull的一点区别。前者表示绝对的“空”,后者则表示“空值”——仍然是一个“值”。null不能作为“值”赋给字段(语句1),DBNull则可以。数据表中的值是不可能为null的,在DGV中编辑单元格时,空串意味着DBNull。
DGV单元格输入空值会怎样?
如果在单元格中输入空内容,则在CellValidating事件中,DataGridViewCellValidatingEventArgs的 FormattedValue属性的值是空字符串,在CellValidated事件中,单元格的值为null。如果DGV已绑定到DataTable,则此单元格向数据表输入的值为DBNull。
复杂情况下慎用组合框列(DataGridViewComboBoxColumn)
我遇到的情况是:
1. DGV绑定到数据源。
2. 某列中的每个单元格都可能具有自己的值列表,取决于所属行的其它单元格的值,就是说当它依赖的单元格的值发生变化时,它的值列表需要调整。
开始时我使用组合框列,碰到的最大的麻烦是单元格的值经常不在它的值列表之内,这会导致异常,说“单元格的值无效。”由于DGV绑定到数据源的机制,有些时机下,情形会复杂到难以控制。当数据源与DGV显示的差异比较大,运行时需要一些代码逻辑实现数据格式化时尤其如此。大量的事件,单独考虑都容易使用,但要在好几个事件中执行相关的几个任务或一个任务的不同阶段,事件的协同变得很困难。这种情况下调试,仿佛自己是个疲于应付的农场主,怎么修理栅栏也不能防止一大群狡猾的野兔溜进来一两个。
DataColumn.Expression的取消
设置数据列的表达式时,其只读属性被自动置为真,所以要取消表达式,除了将数据列的表达式属性置为空串,一般还需要将其只读属性置为假。
DataColumn. ExtendedProperties序列化后
类型为PropertyCollection,派生自HashTable。可以储任何类型的值,但以XML的形式反序列化后,所有项目的数据类型均为字符串。MSDN对此的说明是“扩展属性必须是 String 类型。当以 XML 的形式写 DataColumn 时,不保持非 String 类型的属性。”未见对HashTable有类似说明。
区分UI和数据的只读属性
一般来说UI的只读属性限制的是用户而非程序员,即一个控件是只读的,意味着在运行时用户不能编辑,但控件属性(比如Value)仍可通过代码修改。
而对于DataColumn来说,只读属性的意义与C#中的readonly关键字的意义接近,如果DataColumn是只读的,意味着行一旦被添加到表中,列就不能再更改,即使通过代码也不行,但是初始化是可以的。这个特性正是自动递增列和表达式列需要的。





