DataGridView简介

基本数据绑定

熟悉DataGridView的最佳方法就是实际尝试一下,无需配置任何属性。就像DataGrid一样,您可以使用DataSource属性来绑定DataTable对象(或从DataTable派生的对象)。

Dim ds As DataSet = GetDataSet()

DataGridView1.DataSource = ds.Tables("Customers")

DataGrid不同的是,DataGridView一次只能显示一个表。如果绑定整个DataSet,则不会显示任何数据,除非您使用要显示的表名设置了DataMember属性。

DataGridView1.DataSource = ds

DataGridView1.DataMember = "Customers"

基本的DataGridView显示遵循以下几项简单的规则:

为数据源中的每个字段创建一列。

使用字段名称创建列标题。列标题是固定的,这意味着用户在列表中向下移动时列标题不会滚动出视图。

支持Windows XP视觉样式。您会注意到列标题具有新式的平面外观,并且当用户将鼠标移到其上时会突出显示。

DataGridView还包括几个您可能不会立即注意到的默认行为:

允许就地编辑。用户可以在单元格中双击或按F2来修改当前值。唯一的例外是将DataColumn.ReadOnly设置为True的字段(如当前示例中的OrderID字段)。

支持自动排序。用户可以在列标题中单击一次或两次,基于该字段中的值按升序或降序对值进行排序。默认情况下,排序时会考虑数据类型并按字母或数字顺序进行排序。字母顺序区分大小写。

允许不同类型的选择。用户可以通过单击并拖动来突出显示一个单元格、多个单元格或多个行。单击DataGridView左上角的方块可以选择整个表。

支持自动调整大小功能。用户可以在标题之间的列分隔符上双击,使左边的列自动按照单元格的内容展开或收缩。

美化DataGridView

DataGridView的默认外观仅仅比DataGrid略有改进,但是使用几项快速调整功能,您可以将其显著改进。

其中的一个问题就是列无法自动展开以适合其包含的数据。您可以使用DataGridView.AutoSizeColumns()方法以及DataGridViewAutoSizeColumnCriteria枚举中的某个值来解决此问题。您可以选择根据标题文本、当前显示的行或表中的所有行的的宽度来调整列宽。

'根据标题或此列的某一行中

'最长一段文本的宽度调整

'列宽。

DataGridView1.AutoSizeColumns( _

DataGridViewAutoSizeColumnCriteria.HeaderAndRows)

请记住,此方法必须在绑定数据后调用,否则不会产生任何效果。你可能还需要在用户编辑数据后使用它(可能在响应DataGridView.CellValueChanged等事件时)。

如果不增加列宽,则可以更改行高。默认情况下,列中的文本会跨越多行。如果您使用DataGridView.AutoSizeRows()方法,则行会根据其中的内容调整高度。使用此方法前,您可能希望增加列宽,尤其是在字段包含大量文本时。例如,以下代码片段使说明列的列宽增加为原列宽的四倍,然后调整行高以容纳其内容。

DataGridView.Columns("Description").Width *= 4

DataGridView.AutoSizeRows( _

DataGridViewAutoSizeRowsMode.HeaderAndColumnsAllRows)

图 1比较了自动调整DataGridView大小的各种方法。

另一个合理的更改是清理每一列中显示的标题文本。例如,标题“Order Date”比字段名称“OrderDate”看上去更为专业。这项更改很容易进行。您只需从DataGridView.Columns集合中检索相应的DataGridViewColumn,并修改其HeaderText属性:

DataGridView.Columns("OrderID").HeaderText = "Order ID"

使用DataGridView选择单元格

默认情况下,DataGridView允许自由选择。用户可以突出显示单元格或单元格组,可以一次突出显示所有单元格(通过单击网格右上角的方块),还可以突出显示一行或多行(通过在行标题列中单击)。根据选择模式,用户甚至能够通过选择列标题来选择一列或多列。通过使用DataGridViewSelectionMode枚举中的某个值来设置DataGridView.SelectionMode属性,可以控制此行为,如下所述:

CellSelect:用户可以选择单元格,但不能选择整个行或标题。如果DataGridView.MultiSelectTrue,则用户可以选择多个单元格。

FullColumnSelect:单击列标题只能选择整个列。如果DataGridView.MultiSelectTrue,则用户可以选择多个列。使用此模式时,单击列标题不会对网格进行排序。

FullRowSelect:单击行标题只能选择整个行。如果DataGridView.MultiSelectTrue,则用户可以选择多个行。

ColumnHeaderSelect:用户可以使用CellSelectFullColumnSelect选择模式。使用此模式时,单击列标题不会对网格进行排序。

RowHeaderSelect:用户可以使用CellSelectFullRowSelect选择模式。这是默认的选择模式。

通过DataGridView,可以使用以下三个属性方便地检索选定的单元格:SelectedCellsSelectedRowsSelectedColumns。无论使用的是哪种选择模式,SelectedCells都始终返回DataGridViewCell对象的集合。另一方面,如果使用行标题选择了整个行,则SelectedRows只返回信息,而如果使用列标题选择了整个列,则SelectedColumns也只返回信息。

例如,以下代码片段将检查选定的整个行。只要找到一行,它就会在消息框中显示CustomerID列中的相应值:

For Each SelectedRow As DataGridViewRow In _

DataGridView1.SelectedRows

MessageBox.Show( _

SelectedRow.Cells("CustomerID").Value)

Next

使用CurrentCellCurrentCellAddress属性检索对当前单元格的引用也同样简单。使用DataGridView时,您会注意到当前单元格被一个矩形围住,看起来像是一个用黑色虚线绘制的方框。这就是用户当前所在的位置。

CurrentCellAddress属性是只读的,但是您可以使用CurrentCell以编程方式更改当前位置。完成此操作后,DataGridView将被滚动以使当前位置可见。

'移至第十一行的第四个单元格。

DataGridView.CurrentCell = _

DataGridView.Rows(10).Cells(3)

DataGridView对象

到目前为止,您已经了解了如何与当前选定的一组行、单元格和列进行交互。DataGridView提供了两个关键集合,用于处理整个数据集。这两个集合分别是ColumnsDataGridViewColumn对象的集合)和RowsDataGridViewRow对象的集合,每个对象都引用一组DataGridViewCell对象的集合)。2显示了对象模型。

一般而言,您将使用DataGridViewColumn对象来配置列显示属性、格式设置及标题文本,而使用DataGridViewRowDataGridViewCell对象从绑定的记录中检索实际数据。在DataGridViewCell中修改数据时,处理方式与用户编辑的方式相同:引发相应的DataGridView更改事件,并且修改底层的DataTable

现在您已经了解了DataGridView对象模型,因此可以轻松地创建遍历该表的代码。要选择行、列或单元格,只需找到对应的DataGridViewRowDataGridViewColumnDataGridViewCell对象,并将IsSelected属性设置为True

以下示例以编程方式选择OrderID字段的值小于100的所有行:

For Each Row As DataGridViewRow In DataGridView1.Rows

If Row.Cells("OrderID").Value < 100 Then

Row.Selected = True

End If

Next

值得一提的是,有几个从DataGridViewColumn派生的不同的类。这些类可以控制在单元格中显示和编辑值的方式。.NET包括五个预先创建的DataGridView列类:DataGridViewButtonColumnDataGridViewCheckBoxColumnDataGridViewComboBoxColumnDataGridViewImageColumnDataGridViewTextBoxColumn

DataGridView样式

设计DataGridView时面临的挑战之一就是创建一个格式设置系统,该系统要能够足够灵活地应用不同级别的格式设置,而对于非常大的表又要保持高效。从灵活性角度来看,最好的方法是允许开发人员分别配置每个单元格。但是从效率角度来看,这种方法可能是有害的。包含数千行的表中具有好几万个单元格,维护每个单元格的不同格式肯定会浪费很多内存。

为解决此问题,DataGridView通过DataGridViewCellStyle对象来实现多层模型。DataGridViewCellStyle对象表示单元格的样式,并且包括如颜色、字体、对齐、换行和数据格式等详细信息。您可以创建一个DataGridViewCellStyle来指定整个表的默认格式。此外,还可以指定列、行和各个单元格的默认格式。格式设置的越细致、创建的DataGridViewCellStyle对象越多,该解决方案的可伸缩性也就越小。但是,如果您主要使用基于列和基于行的格式设置,并且只是偶尔设置各个单元格的格式,则与DataGrid相比,DataGridView不需要太多内存。

DataGridView应用格式设置时,将遵循以下优先顺序(从最高到最低):

1. DataGridViewCell.Style

2. DataGridViewRow.DefaultCellStyle

3. DataGridView.AlternatingRowsDefaultCellStyle

4. DataGridView.RowsDefaultCellStyle

5. DataGridViewColumn.DefaultCellStyle

6. DataGridView.DefaultCellStyle

重要的是要清楚:样式对象不是以全有/全无的方式应用的,DataGridView会检查每个属性。例如,假设您的单元格使用DataGridViewCell.Style属性来应用自定义字体,但没有设置自定义前景色。结果,该字体设置将覆盖任何其他样式对象中的字体信息,但如果层次结构中下一个样式对象的前景色不为空,则将从该对象继承前景色(在这种情况下为DataGridViewRow.DefaultCellStyle)。

DataGridViewCellStyle定义了两种格式设置:数据和外观。数据格式设置描述显示数据绑定值之前如何对其进行修改。这种格式设置通常包括使用格式设置字符串将数字或日期值转换为文本。要使用数据格式设置,只需使用DataGridViewCellStyle.Format属性设置格式定义或自定义格式字符串即可。

例如,以下代码片段对UnitCost字段中的所有数字进行格式设置,以将它们显示为货币值,保留两位小数并加上在区域设置中定义的相应货币符号:

DataGridView1.Columns("UnitCost"). _

DefaultCellStyle.Format = "C"

外观格式设置包括颜色和格式等表面细节。例如,以下代码右对齐UnitCost字段、应用粗体并将单元格的背景更改为黄色:

DataGridView1.Columns("UnitCost"). _

DefaultCellStyle.Font = _

New Font(DataGridView.Font, FontStyle.Bold)

DataGridView1.Columns("UnitCost"). _

DefaultCellStyle.Alignment = _

DataGridViewContentAlignment.MiddleRight

DataGridView1.Columns("UnitCost"). _

DefaultCellStyle.BackColor = Color.LightYellow

其他与格式设置相关的属性包括ForeColorSelectionForeColorSelectionBackColorWrapMode(控制文本在空间允许时是跨越多行还是直接截断)及NullValue(将替代Null值的值)。

DataGridView还包括一个设计器,用于在设计时配置列样式。只需从“Properties”(属性)窗口中选择“DataGridView Properties”DataGridView属性)链接,或者从各种预先创建的样式设置中选择“AutoFormat”(自动套用格式)。

自定义单元格格式

单元格格式设置的第一种方法是设置更高级别的DataGridViewDataGridViewColumnDataGridViewRow属性。但是,有时您需要为特定单元格单独设置样式。例如,您可能需要在列中的数据大于或小于某个值时标记该列中的数据。例如,突出显示项目计划列表中已过去的到期日期,或者在销售分析中突出显示负收益率。在这两种情况下,您需要对单独的单元格进行格式设置。

了解DataGridView对象模型后,您可能想要遍历特定列中的单元格集合,以寻找要突出显示的值。这种方法是可行的,但不是最好的方法。关键问题是如果用户编辑了数据,或者如果代码更改了绑定的数据源,不会对单元格的突出显示情况进行相应的更新。

幸运的是,DataGridView针对此目的提供了CellFormatting事件。CellFormatting只在显示单元格值之前引发。通过该事件,可以基于单元格的内容来更新单元格样式。以下示例检查特定的客户并相应地标记单元格:

Private Sub DataGridView1_CellFormatting( _

ByVal sender As System.Object, _

ByVal e As System.Windows.Forms. _

DataGridViewCellFormattingEventArgs) _

Handles DataGridView1.CellFormatting

'检查该列是否正确。

If DataGridView1.Columns(e.ColumnIndex).Name = _

"CustomerID" Then

'检查该值是否正确。

If e.Value = "ALFKI" Then

e.CellStyle.ForeColor = Color.Red

e.CellStyle.BackColor = Color.Yellow

End If

End If

End Sub

样式不是影响网格外观的唯一细节。您还可以隐藏列、在不同位置之间移动列,并冻结列,使这些列在用户滚动到右端时仍然可见。这些功能都是通过DataGridViewColumn类的属性提供的,如下所述:

DisplayIndex:设置列在DataGridView中显示的位置。例如,DisplayIndex值为0的列将自动显示在最左边的列中。如果多个列具有相同的DisplayIndex,则先显示最先出现在该集合中的列。因此,如果您使用DisplayIndex将一列向左移动,则可能还需要设置最左边的列的DisplayIndex,以将其向右移动。最初,DisplayIndexDataGridView.Columns集合中DataGridViewColumn对象的索引相匹配。

Frozen:如果为True,则该列将始终可见并且固定在表的左侧,即使用户为查看其他列而滚动到右侧亦如此。

HeaderText:设置将在列标题中显示的文本。

ResizableMinimumWidth:将Resizable设置为False,以防止用户调整列宽;或者将MinimumWidth设置为允许的最小像素数目。

Visible:要隐藏列,请将此属性设置为False

按钮列

DataGridView提供的一种列是DataGridViewButtonColumn,这种列在每一项旁边显示一个按钮。您可以响应此按钮的单击事件,并使用它启动其他操作或显示新的表单。

以下示例使用按钮文字“Details...”创建简单的按钮列:

'创建按钮列。

Dim Details As New DataGridViewButtonColumn()

Details.Name = "Details"

'关闭数据绑定并显示静态文本。

'(但是,您可以通过设置DataPropertyName

'属性来使用该表中的属性。)

Details.DisplayTextAsFormattedValue = False

Details.Text = "Details..."

'清除标题。

Details.HeaderText = ""

'添加该列。

DataGridView1.Columns.Insert( _

DataGridView1.Columns.Count, Details)

图 3显示了包含新列的网格。以下代码会对任何行中的按钮单击事件做出反应,并显示相应的记录信息:

Private Sub DataGridView1_CellClick( _

ByVal sender As System.Object, _

ByVal e As System.Windows.Forms. _

DataGridViewCellEventArgs) _

Handles DataGridView1.CellClick

If DataGridView1.Columns(e.ColumnIndex).Name = _

"Details" Then

MessageBox.Show("You picked " & _

DataGridView1.Rows(e.RowIndex). _

Cells("CustomerID").Value)

End If

End Sub

比较现实的方案是,在此时创建并显示一个新窗口,并将有关选定记录的信息传递到这个新窗口,以便查询并显示完整的信息。

图像列

DataGridView提供的另一种列是DataGridViewImageColumn,这种列将在单元格边框内显示一个图片。您可以设置DataGridViewImageColumn.Layout属性以便配置图片在单元格中的显示方式:是将单元格扩展到适当的大小,还是将直接剪裁太大的图像。

使用DataGridViewImageColumn的方法有两种。首先,您可以采用与DataGridViewButtonColumn相同的方式手动创建并添加该列。如果您需要显示DataSet本身不提供的相关图像信息,则此列非常有用。例如,您可能需要获取文件名(如ProductPic002.gif),从网络驱动器读取相应的文件,然后在网格中显示该文件。为完成此操作,您需要对DataGridView事件(如CellFormatting)做出反应,以便读取相应行的图片、获取图像数据并使用该列中的Value属性将其插入。

如果DataSet包含不需要任何手动操作的二进制图片数据,那么事情会变得更加简单。例如SQL Server Pubs数据库中的pub_info表,该表包含公司徽标。绑定至此表时,您不需要执行任何额外步骤,DataGridView将自动识别出您正在使用图像,并会创建所需的DataGridViewImageColumn。(有关这项技术的示例,请参阅本文的下载内容。)

编辑DataGridView

众所周知,DataGrid的用户输入功能很不灵活,您几乎没有办法自定义单元格验证方式及错误报告方式。而另一方面,DataGridView允许您通过对在编辑过程的所有阶段中引发的大量不同事件做出反应来控制其行为。

默认情况下,当用户用鼠标双击单元格或按F2键时,DataGridView单元格将进入编辑模式。您还可以通过将DataGridView.EditCellOnEnter属性设置为True,对DataGridView进行配置,以便当用户移到该单元格后,该单元格立即切换到编辑模式。您还可以使用DataGridViewBeginEdit()CancelEdit()CommitEdit()EndEdit()方法通过编程方式开始和停止单元格编辑。用户编辑单元格时,行标题将显示一个铅笔状的编辑图标。

用户可以通过按Esc键来取消编辑。如果将EditCellOnEnter属性设置为True,则单元格仍将处于编辑模式,但是所有更改都将被放弃。要提交更改,用户只需移到新的单元格,或将焦点切换到其他控件。如果您的代码可以移动当前单元格的位置,则此代码也会提交更改。

为防止单元格被编辑,可以设置DataGridViewCellDataGridViewColumnDataGridViewRowDataGridViewReadOnly属性(取决于您是要防止对该单元格进行更改、对该列中的所有单元格进行更改、对该行中的所有单元格进行更改,还是要防止对该表中的所有单元格进行更改)。DataGridView还提供了CellBeginEdit事件,用于取消尝试的编辑。

处理错误

默认情况下,DataGridViewTextBoxColumn允许用户输入任何字符,包括当前单元格中可能不允许使用的那些字符。例如,用户可以在数字字段中键入非数字字符,也可以指定与DataSet中定义的ForeignKeyConstraintUniqueConstraint冲突的值。DataGridView采用不同的方式来处理这些问题:

如果可以将编辑的值转换为所需的数据类型(例如,用户在数字列中键入了文本),则用户将不能提交更改或导航到其他行。而必须取消更改或编辑值。

如果编辑的值与DataSet中的约束冲突,则用户通过导航到其他行或按Enter键提交更改后,更改将被立即取消。

这些处理方式适用于大多数情况。但是,如果需要,您也可以通过响应DataGridView.DataError事件来参与错误处理,该事件是在DataGridView侦听到来自数据源的错误时引发的。

验证输入

验证是一项与错误处理稍有不同的任务。通过错误处理,您可以处理由DataSet报告的问题。而通过验证,您可以捕获您自己定义的错误情况,例如DataSet中允许的数据在应用程序中却没有意义。

当用户通过导航到新的单元格提交更改时,DataGridView控件将引发CellValidatingCellValidated事件。这些事件之后是RowValidatingRowValidated事件。您可以响应这些事件,检查用户输入的值是否正确,并执行所需的任何后期处理。如果值无效,通常您会通过两种方式来做出响应,即取消更改和单元格导航(通过将EventArgs对象的Cancel属性设置为True),或者设置某种错误文本来提醒用户。可以将错误文本置于其他控件中,也可以使用相应的DataGridViewRowDataGridViewCellErrorText属性在DataGrid中显示错误文本:

设置DataGridViewCell.ErrorText时,单元格中将显示感叹号图标。将鼠标悬停在此图标上将显示错误消息。

设置DataGridViewRow.ErrorText时,行左侧的行标题中将显示感叹号图标。将鼠标悬停在此图标上将显示错误消息。

通常,您会结合使用这两种属性,并设置行和单元格中的错误消息。以下示例检查CompanyName字段中太长的文本输入。如果发现有问题的值,则会将错误符号(红色的感叹号)添加到单元格中,并显示描述该问题的工具提示文本。

Private Sub DataGridView1_CellValidating( _

ByVal sender As System.Object, _

ByVal e As System.Windows.Forms. _

DataGridViewCellValidatingEventArgs) _

Handles DataGridView1.CellValidating

If DataGridView1.Columns(e.ColumnIndex).Name = _

"CompanyName" Then

If CType(e.FormattedValue, String).Length > _

50 Then

DataGridView1.Rows( _

e.RowIndex).Cells(e.ColumnIndex). _

ErrorText = _

"The company name is too long."

End If

End If

End Sub

使用列表列约束选择

使用验证可以捕获任何错误情况。但是,这种方法不一定是最好的,因为它允许用户输入无效的内容,然后在事实出现后尝试改正无效输入。更好的解决方案是使用户在一开始就无法输入任何无效的内容。

一个常见例子就是您需要将列限制在预定义值列表的范围内。在此示例中,对于用户而言,最简单的办法是从列表中选择正确的值,而不要手动键入值。最大的优势在于,您可以使用DataGridViewComboBoxColumn非常方便地实现此设计。

可以使用Items集合手动为DataGridViewComboBoxColumn添加项列表,就像使用ListBox一样。此外,还可以将DataGridViewComboBoxColumn绑定到其他数据源。在这种情况下,您可以使用DataSource属性来指定数据源,并使用DisplayMember属性指示列中应显示的值,以及使用ValueMember属性指定底层列值应使用的值。

为了演示这种情况,来看看下一个有关Products表的示例。此表中的每一条记录都通过其CategoryID字段链接至Apple-conve

你可能感兴趣的:(设计模式,数据结构,编程,windows,网络应用)