如何于DataGridView中使用计算字段
图表1
图表2
读者呆呆询问了好几次如何于DataGridView控件中使用计算字段的问题。在此我们就通过一个完整的范例一次清楚呈现。事实上,如果绑定至一个数据来源的DataGridView控件还必须内含计算字段的话,必须让DataGridView控件采用虚拟模式(也就是必须将 VirtualMode 属性设定成 True),而且您必须自行在CellValueNeeded事件处理例程中完成运算字段值的撷取作业。
图表1所示者是我们所撰写的程序范例,您可以在其中的DataGridView控件中新增、修改与删除数据,重点在于,其中的「年龄」资料行是根据「出生日期」计算而来。而且每当您更改「出生日期」数据行中的日期值,则在移出该储存格(Cell)后,年龄就会立即计算出来,即使是新增的数据记录也会如此。以下我们说明本程序范例的设计技巧。
首先,您必须如图表2所示,在窗体上加入所需的各个控件,然后撰写下列程序代码。我们是将窗体的程序代码全数列出,其中已加上完整的批注,另外,本程序使用到frmStatus.vb,因此务必记得将此文件汇入您的项目中:
Option Strict On
Imports System.Data.SqlClient
Imports System.IO
Public Class CH13_DemoForm001
Private myDataSet As DataSet
Private Sub CH13_DemoForm001_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' 呼叫 LoadDataToDataSet() 函式来连接至数据来源并传回所需的 DataSet 对象。
myDataSet = LoadDataToDataSet()
If myDataSet IsNot Nothing Then
' 将 BindingSource 组件系结至数据集对象中的「章立民研究室」数据表。
Me.BindingSource1.DataMember = "章立民研究室"
Me.BindingSource1.DataSource = myDataSet
' 将 BindingNavigator 控件的数据来源也设定成 BindingSource 组件,如此一来,
' 就可以使用 BindingNavigator 控件去导览 DataGridView 控件中的数据列。
Me.BindingNavigator1.BindingSource = Me.BindingSource1
' 自订 DataGridView 控件。
CustomizeMyDataGridView()
End If
End Sub
Private Sub CustomizeMyDataGridView()
' 由于我们要自订各个数据行类型,因此必须
' 将 AutoGenerateColumns 属性设定成 False。
DataGridView1.AutoGenerateColumns = False
' 设定奇数资料列的背景色。
DataGridView1.AlternatingRowsDefaultCellStyle.BackColor = _
SystemColors.InactiveCaptionText
' 设定用户一次只能选取一个储存格、数据列或数据行。
DataGridView1.MultiSelect = False
' 设定采用储存格选取模式。
DataGridView1.SelectionMode = DataGridViewSelectionMode.CellSelect
' 设定数据列的高度。
Me.DataGridView1.RowTemplate.Height = 30
' 由于我们的 DataGridView 控件将会内含未系结数据行,因此必须
' 将 VirtualMode 属性设定成True,也就是必须启用虚拟模式。
DataGridView1.VirtualMode = True
' 将 DataGridView 控件的数据来源设定成 BindingSource 组件。
Me.DataGridView1.DataSource = Me.BindingSource1
' 接下来的程序代码要自订各个数据行类型.....
'
' 资料行:员工编号,文字方块
'
' 建立一个 DataGridViewTextBoxColumn 对象并设定其相关属性。
'
Dim colEmployeeId As New DataGridViewTextBoxColumn()
' 设定来源字段。
colEmployeeId.DataPropertyName = "员工编号"
' 设定数据行标题。
colEmployeeId.HeaderText = "编号"
colEmployeeId.Name = "员工编号"
' 将此数据行设定成只读的。
colEmployeeId.ReadOnly = True
' 设定数据行的宽度。
colEmployeeId.Width = 60
' 将 DataGridViewTextBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colEmployeeId)
'
' 资料行:姓名,文字方块
'
' 建立一个 DataGridViewTextBoxColumn 对象并设定其相关属性。
'
Dim colName As New DataGridViewTextBoxColumn()
' 调整资料行的宽度使其足以显示出最宽的可见储存格(包括标题在内)。
colName.AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells
' 设定来源字段。
colName.DataPropertyName = "姓名"
' 设定数据行标题。
colName.HeaderText = "员工姓名"
colName.Name = "姓名"
colName.ReadOnly = False
' 将 DataGridViewTextBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colName)
'
' 资料行:性别,下拉式清单方块
'
' 建立一个 DataGridViewComboBoxColumn 对象并设定其相关属性。
'
Dim colGender As New DataGridViewComboBoxColumn()
' 调整资料行的宽度使其足以显示出标题。
colGender.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
' 设定来源字段。
colGender.DataPropertyName = "性别"
' 设定下拉式清单中的选项。
colGender.Items.AddRange(New String() {"男", "女"})
' 排序下拉式清单方块的内容。
colGender.Sorted = True
' 停用数据行的排序功能。
colGender.SortMode = DataGridViewColumnSortMode.NotSortable
colGender.HeaderText = "性别"
colGender.Name = "性别"
colGender.ReadOnly = False
' 将 DataGridViewComboBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colGender)
'
' 资料行:出生日期,自订格式化的文字方块
'
' 建立一个 DataGridViewTextBoxColumn 对象并设定其相关属性。
'
Dim colBirthday As New DataGridViewTextBoxColumn()
' 调整资料行的宽度使其足以显示出最宽的可见储存格(包括标题在内)。
colBirthday.AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells
' 设定来源字段。
colBirthday.DataPropertyName = "出生日期"
' 设定日期显示格式。
colBirthday.DefaultCellStyle.Format = "MM-dd-yyyy"
colBirthday.HeaderText = "出生月日年"
colBirthday.Name = "出生日期"
' 将 DataGridViewTextBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colBirthday)
'
' 资料行:年龄,未系结的资料行
'
' 建立一个 DataGridViewTextBoxColumn 对象并设定其相关属性。
'
Dim colAge As New DataGridViewTextBoxColumn()
' 调整资料行的宽度使其足以显示出标题。
colAge.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
colAge.HeaderText = "年龄"
colAge.Name = "年龄"
' 将此资料行设定成只读,毕竟年龄是根据出生日期所计算出来。
' 如果没有将此数据行设定成只读,则必须通过 CellValuePush 事件处理例程
' 将所输入的数据写入数据来源。
colAge.ReadOnly = True
' 设定当引发 DataGridView 控件的 CellValueNeeded 事件时所要执行的
' 事件处理例程。请特别注意,未系结资料行是通过CellValueNeeded 事件
' 来撷取资料。
AddHandler DataGridView1.CellValueNeeded, AddressOf colAge_CellValueNeeded
' 将 DataGridViewTextBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colAge)
'
' 资料行:婚姻状况,下拉式清单方块
'
' 建立一个 DataGridViewComboBoxColumn 对象并设定其相关属性。
'
Dim colMaritalStatus As New DataGridViewComboBoxColumn()
' 调整资料行的宽度使其足以显示出标题。
colMaritalStatus.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader
' 设定来源字段。
colMaritalStatus.DataPropertyName = "婚姻状况"
' 设定下拉式清单中的选项。
colMaritalStatus.Items.AddRange(New String() {"已婚", "未婚"})
' 排序下拉式清单方块的内容。
colMaritalStatus.Sorted = True
' 停用数据行的排序功能。
colMaritalStatus.SortMode = DataGridViewColumnSortMode.NotSortable
colMaritalStatus.HeaderText = "婚姻状况"
colMaritalStatus.Name = "婚姻状况"
colMaritalStatus.ReadOnly = False
' 将 DataGridViewTextBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colMaritalStatus)
'
' 资料行:部门,下拉式清单方块
'
' 建立一个 DataGridViewComboBoxColumn 对象并设定其相关属性。
'
' 请注意,此数据行之下拉式清单方块的选项内容是来自数据集 myDataSet
' 当中的「公司部门」资料表。
'
Dim colDepartment As New DataGridViewComboBoxColumn()
colDepartment.DataPropertyName = "部门"
' 将下拉式清单方块系结至数据集 myDataSet 当中的「公司部门」资料表。
colDepartment.DataSource = myDataSet.Tables("公司部门")
colDepartment.ValueMember = "部门"
colDepartment.DisplayMember = "部门"
colDepartment.HeaderText = "任职部门"
colDepartment.Name = "部门"
colDepartment.ReadOnly = False
colDepartment.Width = 120
' 将 DataGridViewComboBoxColumn 对象新增至 DataGridView 控件的数据行集合中。
DataGridView1.Columns.Add(colDepartment)
End Sub
' 此事件处理例程负责替未系结数据行撷取数据。
Private Sub colAge_CellValueNeeded(ByVal sender As Object, _
ByVal e As DataGridViewCellValueEventArgs)
If e.ColumnIndex = CType(sender, DataGridView).Columns("年龄").Index Then
Dim age As Integer
Dim birthDate As DateTime = _
CDate(CType(sender, DataGridView).Rows( _
e.RowIndex).Cells("出生日期").Value)
age = DateTime.Today.Year - birthDate.Year
If (DateTime.Today.DayOfYear < birthDate.DayOfYear) Then
age -= 1
End If
e.Value = age
End If
End Sub
Private Sub DataGridView1_CellEndEdit(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
Handles DataGridView1.CellEndEdit
If e.ColumnIndex = CType(sender, DataGridView).Columns("出生日期").Index Then
Dim age As Integer
Dim birthDate As DateTime = CDate(CType( _
sender, DataGridView).Rows(e.RowIndex).Cells("出生日期").Value)
age = DateTime.Today.Year - birthDate.Year
If (DateTime.Today.DayOfYear < birthDate.DayOfYear) Then
age -= 1
End If
CType(sender, DataGridView).Rows( _
e.RowIndex).Cells("年龄").Value = age.ToString
End If
End Sub
' 本程序会连接至数据来源并建立所需的 DataSet 对象。
Private Function LoadDataToDataSet() As DataSet
' 利用 SqlConnectionStringBuilder 对象来构建连接字符串。
Dim sqlStringBuilder As New SqlConnectionStringBuilder()
sqlStringBuilder.DataSource = "(local)\SQLEXPRESS"
sqlStringBuilder.InitialCatalog = "北风贸易"
sqlStringBuilder.IntegratedSecurity = True
' 建立一个数据集。
Dim ds As New DataSet()
' 显示一个状态讯息对话框来表示我们目前要尝试连结至 SQL Server Express。
Dim frmStatusMessage As New frmStatus
frmStatusMessage.Show("连接至SQL Server Express 中....")
Try
Using northwindConnection As _
New SqlConnection(sqlStringBuilder.ConnectionString)
Dim cmdLiming As New SqlCommand( _
"SELECT DISTINCT 部门FROM dbo.章立民研究室;" & _
"SELECT 员工编号,姓名,性别,婚姻状况,部门,出生日期,玉照 " & _
"FROM dbo.章立民研究室", northwindConnection)
northwindConnection.Open()
Using drLiming As SqlDataReader = cmdLiming.ExecuteReader()
ds.Load( _
drLiming, _
LoadOption.OverwriteChanges, _
New String() {"公司部门", "章立民研究室"})
End Using
End Using
' 设定「出生日期」字段的默认值。
ds.Tables("章立民研究室").Columns("出生日期").DefaultValue = DateTime.Now
Catch exc As Exception
frmStatusMessage.Close()
MessageBox.Show( _
"要能够顺利执行本范例程序,您的计算机必须已安装SQL Server " & _
"Express,并且必须已附加了本书所附的「北风贸易」数据库。" & _
"关于如何安装SQL Server Express,请参阅附录或相关文件说明。")
' 无法连接至SQL Server。
Return Nothing
End Try
frmStatusMessage.Close()
Return ds
End Function
End Class