本课学习目标
- 认识 DataGridView 的用途。
- 使用 DataTable 在内存中保存查询结果。
- 使用 SqlDataAdapter.Fill() 把 SELECT 结果装入 DataTable。
- 通过 DataGridView.DataSource 绑定数据。
- 设置 AutoSizeColumnsMode 和 SelectionMode。
- 隐藏或显示 Row Headers。
- 修改 DataGridView 列标题。
- 设置 Score 列的显示格式。
- 使用 CellClick 取得选中行。
- 把选中行数据带回 TextBox。
- 让修改与删除操作直接使用当前选中记录。
26.1 为什么使用 DataGridView?
数据库记录天然是“行与列”的结构,DataGridView 正好可以把这种结构直接显示给用户。
一次看到多笔记录
比 TextBox 一行一行拼接更容易阅读。
字段结构清楚
StudentID、Name、Course、Score 各自占一列。
直接点选某一行
选中后可以继续修改或删除该记录。
26.2 建立 StudentCrudGrid2026
建立新的 Windows Forms 项目:
StudentCrudGrid2026
继续使用:
StudentManagementDB
dbo.Students
DatabaseHelper.vb
Microsoft.Data.SqlClient
26.3 设计 Form1
| Student ID | 姓名 | 课程 | 成绩 |
|---|---|---|---|
| 1 | 李明 | Visual Basic 2026 | 88.00 |
| 2 | 王芳 | Visual Basic 2026 | 82.00 |
| 3 | 陈伟 | Python Programming | 91.00 |
| 控件 | Name | 设置 |
|---|---|---|
| TextBox | txtStudentID | ReadOnly = True |
| TextBox | txtName | 学生姓名 |
| TextBox | txtCourse | 课程名称 |
| TextBox | txtScore | 成绩 |
| Button | btnAdd | 新增 |
| Button | btnRefresh | 刷新 |
| Button | btnUpdate | 修改 |
| Button | btnDelete | 删除 |
| Button | btnClear | 清除 |
| DataGridView | dgvStudents | 显示数据库记录 |
| Label | lblStatus | 显示状态 |
26.4 DataGridView 建议属性
26.5 什么是 DataTable?
DataTable 可以理解为内存中的表格。它包含列和行,非常适合作为 DataGridView 的数据来源。
26.6 使用 SqlDataAdapter
Form1.vb 顶部:
Imports Microsoft.Data.SqlClient
Imports System.Data
读取学生:
Private Sub LoadStudents()
Const sql As String =
"SELECT StudentID, Name, Course, Score " &
"FROM dbo.Students " &
"ORDER BY StudentID;"
Using connection As New SqlConnection(
DatabaseHelper.ConnectionString)
Using adapter As New SqlDataAdapter(
sql,
connection)
Dim table As New DataTable()
adapter.Fill(table)
dgvStudents.DataSource =
table
End Using
End Using
End Sub
26.7 DataSource 数据绑定
dgvStudents.DataSource =
table
这叫数据绑定(Data Binding)。DataGridView 根据 DataTable 的列自动产生相应的显示列。
使用 SqlDataReader 逐行读取,再自己拼接到 txtOutput。
使用 SqlDataAdapter 填充 DataTable,再直接绑定到 DataGridView。
26.8 设置列标题和格式
LoadStudents 完成绑定后,可以调用:
ConfigureStudentGrid()
建立方法:
Private Sub ConfigureStudentGrid()
If dgvStudents.Columns.Count = 0 Then
Exit Sub
End If
dgvStudents.Columns(
"StudentID").HeaderText =
"Student ID"
dgvStudents.Columns(
"Name").HeaderText =
"姓名"
dgvStudents.Columns(
"Course").HeaderText =
"课程"
dgvStudents.Columns(
"Score").HeaderText =
"成绩"
dgvStudents.Columns(
"Score").
DefaultCellStyle.Format =
"N2"
End Sub
26.9 完整 LoadStudents
Private Sub LoadStudents()
Const sql As String =
"SELECT StudentID, Name, Course, Score " &
"FROM dbo.Students " &
"ORDER BY StudentID;"
Using connection As New SqlConnection(
DatabaseHelper.ConnectionString)
Using adapter As New SqlDataAdapter(
sql,
connection)
Dim table As New DataTable()
adapter.Fill(table)
dgvStudents.DataSource =
table
ConfigureStudentGrid()
lblStatus.Text =
$"已加载 {table.Rows.Count} 笔学生资料。"
End Using
End Using
End Sub
26.10 刷新按钮
Private Sub btnRefresh_Click(
sender As Object,
e As EventArgs) Handles btnRefresh.Click
Try
LoadStudents()
Catch ex As SqlException
MessageBox.Show(
ex.Message,
"数据库错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
26.11 点击表格取得选中记录
DataGridView 的 CellClick 事件可以知道用户点击了哪一行。
Private Sub dgvStudents_CellClick(
sender As Object,
e As DataGridViewCellEventArgs) _
Handles dgvStudents.CellClick
If e.RowIndex < 0 Then
Exit Sub
End If
Dim row As DataGridViewRow =
dgvStudents.Rows(
e.RowIndex)
txtStudentID.Text =
row.Cells(
"StudentID").Value.ToString()
txtName.Text =
row.Cells(
"Name").Value.ToString()
txtCourse.Text =
row.Cells(
"Course").Value.ToString()
txtScore.Text =
row.Cells(
"Score").Value.ToString()
lblStatus.Text =
$"已选择 Student ID {txtStudentID.Text}。"
End Sub
26.12 为什么检查 e.RowIndex < 0?
当用户点击列标题时,RowIndex 可能不是实际数据行,因此先检查:
If e.RowIndex < 0 Then
Exit Sub
End If
这样可以避免把表头当成学生记录。
26.13 StudentID 改成只读
这一课不再让用户手工输入 StudentID,而是从 DataGridView 的选中行取得。
txtStudentID.ReadOnly =
True
这样可以减少输入错误,也更符合数据库应用的实际操作习惯。
26.14 新增学生后自动刷新 DataGridView
继续沿用 Lesson 25 的 InsertStudent 方法。新增成功后:
InsertStudent(
studentName,
course,
score)
ClearInputFields()
LoadStudents()
DataGridView 会马上显示新记录。
26.15 修改当前选中学生
Private Sub btnUpdate_Click(
sender As Object,
e As EventArgs) Handles btnUpdate.Click
Dim studentId As Integer
If Not Integer.TryParse(
txtStudentID.Text,
studentId) Then
MessageBox.Show(
"请先在表格中选择学生。")
Exit Sub
End If
Dim studentName As String = ""
Dim course As String = ""
Dim score As Decimal
If Not TryGetStudentInput(
studentName,
course,
score) Then
Exit Sub
End If
Try
Dim rowsAffected As Integer =
UpdateStudent(
studentId,
studentName,
course,
score)
If rowsAffected = 1 Then
lblStatus.Text =
"学生资料修改成功。"
ClearInputFields()
LoadStudents()
Else
MessageBox.Show(
"找不到要修改的学生。")
End If
Catch ex As SqlException
MessageBox.Show(
ex.Message,
"数据库错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
26.16 删除当前选中学生
Private Sub btnDelete_Click(
sender As Object,
e As EventArgs) Handles btnDelete.Click
Dim studentId As Integer
If Not Integer.TryParse(
txtStudentID.Text,
studentId) Then
MessageBox.Show(
"请先在表格中选择学生。")
Exit Sub
End If
Dim result As DialogResult =
MessageBox.Show(
$"确定要删除 Student ID {studentId} 吗?",
"确认删除",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning)
If result <> DialogResult.Yes Then
Exit Sub
End If
Try
Dim rowsAffected As Integer =
DeleteStudent(
studentId)
If rowsAffected = 1 Then
lblStatus.Text =
"学生资料删除成功。"
ClearInputFields()
LoadStudents()
Else
MessageBox.Show(
"找不到要删除的学生。")
End If
Catch ex As SqlException
MessageBox.Show(
ex.Message,
"数据库错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
26.17 清除输入与取消选择
Private Sub ClearInputFields()
txtStudentID.Clear()
txtName.Clear()
txtCourse.Clear()
txtScore.Clear()
dgvStudents.ClearSelection()
txtName.Focus()
End Sub
26.18 Form.Load 初始化表格
Private Sub Form1_Load(
sender As Object,
e As EventArgs) Handles MyBase.Load
dgvStudents.ReadOnly =
True
dgvStudents.SelectionMode =
DataGridViewSelectionMode.FullRowSelect
dgvStudents.MultiSelect =
False
dgvStudents.AllowUserToAddRows =
False
dgvStudents.AllowUserToDeleteRows =
False
dgvStudents.AutoSizeColumnsMode =
DataGridViewAutoSizeColumnsMode.Fill
dgvStudents.RowHeadersVisible =
False
Try
LoadStudents()
dgvStudents.ClearSelection()
Catch ex As SqlException
MessageBox.Show(
ex.Message,
"数据库错误",
MessageBoxButtons.OK,
MessageBoxIcon.Error)
End Try
End Sub
26.19 Data Binding 与手工填充
把 DataTable 直接交给 DataGridView,代码少、列结构自动产生,适合本课。
自己定义每一列和每一行,控制更细,但代码更多。复杂界面有时会使用。
这一课使用 DataTable + DataSource,是数据库数据显示最容易理解的路线。
26.20 本课的数据流
查询数据库。
把结果填入 DataTable。
把 DataTable 绑定到 dgvStudents。
CellClick 把数据带回 TextBox。
使用选中记录的 StudentID 操作数据库。
26.21 初学者常见错误
忘记设置 DataSource
Fill 只是把资料放入 DataTable;还要把 DataTable 指定给 dgvStudents.DataSource。
点击表头也读取数据
CellClick 中必须先检查 e.RowIndex < 0。
更新后没有重新 LoadStudents
数据库已经改变,但表格还显示旧数据。CRUD 后应刷新。
允许用户直接修改 StudentID
主键应来自选中记录,txtStudentID 设置 ReadOnly 更安全。
26.22 小练习
- 把 Score 列标题改成“成绩(%)”。
- 让 StudentID 列宽比 Course 列更窄。
- 在 lblStatus 显示当前选中学生姓名。
- 加入“双击行”事件,双击后弹出该学生摘要。
- 加入一个按钮,只重新刷新 DataGridView,不清除 TextBox。
- 把 DataGridView 的交替行背景效果交给 Designer 设置,观察可读性。
- 尝试使用 dgvStudents.CurrentRow 取得当前行,并比较与 CellClick 的差异。
本课复习
- DataGridView 的主要用途是什么?
- DataTable 是什么?
- SqlDataAdapter.Fill() 做什么?
- DataSource 的作用是什么?
- 为什么本课把 DataGridView 设置为 ReadOnly?
- FullRowSelect 有什么效果?
- 为什么 CellClick 要检查 e.RowIndex?
- 怎样把当前行的 StudentID 放到 txtStudentID?
- 为什么 CRUD 完成后通常要重新 LoadStudents?
- 为什么 txtStudentID 应该设为 ReadOnly?