1 操作数据(Working with Data)
多数情况下,使用DataGridView的时候都需要跟数据打交道,这时有很多事情可能需要你去做。你需要验证用户输入的数据,或者需要对数据进行格式化。DataGridView能够以三种模式显示数据:bound、unboundand 和virtual。每种模式都有自己的特性和存在的理由。不管是否是数据绑定模式,在操作数据时,如果发生错误,DataGridView通常会触发DataError事件,理解该事件发生的原因能让你更好地利用它。
1.1 数据输入和验证的相关事件
用户输入数据时-对其所在的行或单元格,你可能希望验证这些数据,在遇到无效数据时通知用户。就像常见的Windows Forms控件,DataGridView的行和单元格也有Validating和Validated事件,验证事件可被取消。用户在单元格/行间移动时会触发Enter和Leave事件。最后,用户在开始编辑单元格时也会触发事件。了解所有这些程序的发生顺序会对你很有帮助。
1.1.1 数据验证相关事件的顺序
下面列出validation,enter/leave和begin/end这些事件的顺序(当EditMode为EditOnEnter时):
当从一个单元格移动至另一单元格(在同一行内):
1) Cell Leave (原来的单元格)
2) Cell Validating/ed (原来的单元格)
3) Cell EndEdit (原来的单元格)
4) Cell Enter (新的单元格)
5) Cell BeginEdit (新的单元格)
当从一行移动到另一行:
1) Cell Leave (原来的单元格),Row leave (原来的行)
2) Cell Validating/ed (原来的单元格)
3) Cell EndEdit (原来的单元格)
4) Row Validating/ed (原来的行)
5) Row Enter (新的行)
6) Cell Enter (新的单元格)
7) Cell BeginEdit (新的单元格)
1.1.2 验证数据
验证用户输入时,如果DataGridView采用非数据绑定模式,通常会对单元格进行验证;而如果采用数据绑定模式,则一般会对行进行验证。这与数据的组织方式密切相关,非数据绑定模式下,一行的单元格间关系一般比较“散”,而绑定模式下,数据源的数据一般以行来组织。但有时在数据绑定模式下会同时进行单元格级和行级的验证。
1.1.2.1 显示错误信息
一旦遭遇了无效的输入数据,你通常需要通知用户。这时有多种方式可以选择,传统的方式是使用信息对话框。DataGridView还能够为行或单元格显示一个错误图标来通知用户输入了无效数据。错误图标带有一个工具提示,它提供了该错误的相关信息:
1.1.2.2 常见问题(FAQ)
1.1.3 在新行中的数据输入(Data Entry in the New Row)
当在程序中使用DataGridView来编辑数据时,你往往希望提供让用户添加新行数据的功能。DataGridView控件支持这个功能,提供了一个用于添加新记录的行,而这一行总是显示为最后一行,并在该行的标题单元格标以星号(*)。 下面的几个小节会讨论一些在程序中使用这个新行时需要考虑的内容。(下面总是以 新行 表示 用于添加新记录的行 )
1.1.3.1 显示新行
使用AllowUserToAddRows属性以指示是否显示新行,其默认值为true。
新行处于网格的最后一行,标题带有星号:
在数据绑定的情况下,当DataGridView控件的AllowUserToAddRows属性和数据源的IBindingList.AllowNew 属性都为true时,新行才会显示,只要两者有一个为false,新行就不会显示。
1.1.3.2 为生成的新行添加默认值
当用户选择新行作为当前行,DataGridView会触发DefaultValuesNeeded事件。在该事件中可以访问新行,并为其生成默认值,为用户输入提供方便。
下面这段代码演示了如何在DefaultValuesNeeded事件中为新行指定默认值。
private void dataGridView1_DefaultValuesNeeded(object sender,
DataGridViewRowEventArgs e)
{
e.Row.Cells["Region"].Value = "WA";
e.Row.Cells["City"].Value = "Redmond";
e.Row.Cells["PostalCode"].Value = "98052-6399";
e.Row.Cells["Region"].Value = "NA";
e.Row.Cells["Country"].Value = "USA";
e.Row.Cells["CustomerID"].Value = NewCustomerId();
}
1.1.3.3 Rows集合与新行的关系
新行包含在DataGridView控件的Rows集合中,又因其总是处于最后一行,下面这行代码会返回新行:
DataGridViewRow row = dataGridView1.Rows[dataGridView1.Rows.Count - 1];
尽管新行也包含在Rows集合中,它与Rows集合中其它行的行为却不相同,表现在两点:
- 不能以编程的方式将新行从Rows集合中移除,如果你尝试这么做,会抛出InvalidOperationException类型的异常。用户也不能删除新行。DataGridViewRowCollection.Clear()方法也不能将新行从Rows集合中移除。
- 不能在新行之后添加行。如果你尝试这么做,会抛出InvalidOperationException 类型的异常。这种特性的结果是,新行总处于DataGridView的最后一行。当新行显示的时候,DataGridViewRowCollection 类中用于添加行的方法-Add,AddCopy以及AddCopies-在内部都调用用于插入的方法。
1.1.3.4 在新行中输入数据
用户开始在新行输入数据之前,新行的IsNewRow属性值为true;一旦用户开始输入,这一行就不再是新行了,DataGridView中会产生一个“新”的新行,看下面示意图:
在添加“新”的新行时,会触发UserAddedRow事件,它的事件处理函数的第二个参数有属性Row,指定了这个“新”的新行。如果用户此时按下Escape键,“新”的新行会被移除,这会触发UserDeletingRow事件,它的事件处理函数的第二个参数的属性Row指定了“新”的新行。
1.1.3.5 自定义新行的可视化效果
新行是基于RowTemplate模板创建的,如果没有指定它的单元格的样式,它们会采用继承的样式。要了解样式继承的更多信息,请参看第五章第一节的内容。
新行中单元格的初始值是由每个单元格的DefaultNewRowValue属性决定的。对于DataGridViewImageCell类型的单元格,其初始值为一个占位图片,其它类型的则为null。你可以重写这个属性以返回自定义值。但也可以在DefaultValuesNeeded事件处理函数中对默认值进行替换,该事件在焦点进入新行时触发。
新行标题的标准图标是箭头或者星号,并没有得到暴露。如果你要自定义这个图标,就需要创建一个自定义的DataGridViewRowHeaderCell 类。
新行的标题的标准图标使用标题单元格DataGridViewCellStyle的ForeColor属性。注意:如果没有足够的空间,图标就不会再显示。
如果为标题单元格设置了字符串值(通过Value属性),但没有足够的控件同时显示文本和图标,那么图标会被首先截掉。
1.1.3.6 新行的排序
在非绑定模式下,新行总是添加在DataGridView的最后一行,即使已经对数据排序。用户需要在添加新行后再次进行排序,以将新记录放在合适的位置;这种行为方式类似于ListView控件。
在绑定模式或虚拟模式(Virtual Mode)下,如果已对数据排序,那么插入数据时的行为取决于数据模型的实现方式。对于ADO.NET,新加的行会被自动排序至合适的位置。
1.1.3.7 关于新行,还要注意:
你不能将新行的Visible属性值设置为false,否则会触发一个InvalidOperationException类型的异常。
新行在创建时总是处于非选中(unselected)状态。
1.1.3.8 Virtual Mode下的新行
如果你正要实现虚拟模式(Virtual Mode),需要考虑数据模型添加新行和回滚添加操作的情况。该功能准确的实现方式取决于数据模型的实现方式及其事务机制,例如,提交的时候是针对单元格还是行。参看本文档后面关于Virtual Mode的主题。
1.2 关于Null值
在使用数据源的时候,比如数据库或业务对象,经常需要处理null值。null值可能是一个实际的null(VB中为Nothing),也可能是一个数据库的”null”值(DBNull.Value),当你遭遇了这些值,就需要考虑如何显示它们。另一方面,很多时候,你还需要向数据源写入null值。使用单元格Style的NullValue属性和DataSourceNullValue 属性,你可以改变DataGridView处理null值的方式。
1.2.1 NullValue属性
DataGridViewCellStyle.NullValue 属性本来要被命名为FormattedNullValue 的,但是后来没来得及作出这个更改。但它能给我们带来一点提示——顾名思义,在格式化时会用到它。如果一个单元格的值为”null”(等于null或DBNull.Value),它会使用你设置的NullValue属性来显示。该属性的默认值取决于所在列的类型,见下图:
DataGridView列类型 |
列的DefaultCellStyle.NullValue值 |
TextBoxColumn |
String.Empty (“”) |
ImageColumn |
空的图像( ) |
ComboBoxColumn |
String.Empty (“”) |
ButtonColumn |
String.Empty (“”) |
LinkColumn |
String.Empty (“”) |
CheckBoxColumn |
默认值取决于ThreeState属性的值,如果为true,默认值为CheckState.Indeterminate ,否则为unchecked。 |
有一点要了解,在用户输入数据时也会用到NullValue。例如,若用户向TextBox类型单元格输入了string.Empty,那么会将null作为该单元格的值。 查看下面的DataSourceNullValue属性以了解究竟是输入了什么作为单元格的值。
1.2.2 DataSourceNullValue属性
DataGridViewCellStyle.DataSourceNullValue属性要被命名为ParseNullValue的,如果NullValue属性被命名为FormattedNullValue的话,但最后还是采用了DataSourceNullValue,这样更直观准确。在将null值写入单元格的值时,就会用到DataSourceNullValue属性。在数据绑定情形下,这个null值将被写入数据库或业务对象,此处需要进行控制,因为对于数据库和业务对象来说,null的概念不尽相同。通常你会期望,使用业务对象时将DataSourceNullValue 设置为null,而使用数据库时则将其设置为DBNullValue。DataSourceNullValue的默认值为DBNull.Value。