VB Visual Basic 2026 中文教程
第 26 课 · 数据表格显示

使用 DataGridView 显示数据

上一课已经完成数据库 CRUD,但所有记录只是输出到多行 TextBox。真正的数据库桌面应用通常使用 DataGridView 以表格方式显示数据。本课将学习 DataTable、SqlDataAdapter 与 DataSource,并让用户直接点击表格中的学生记录,把资料带回 TextBox 进行修改或删除。

环境:Visual Studio 2026 · VB.NET · .NET 10数据库:StudentManagementDB实作:StudentCrudGrid2026

本课学习目标

  • 认识 DataGridView 的用途。
  • 使用 DataTable 在内存中保存查询结果。
  • 使用 SqlDataAdapter.Fill() 把 SELECT 结果装入 DataTable。
  • 通过 DataGridView.DataSource 绑定数据。
  • 设置 AutoSizeColumnsModeSelectionMode
  • 隐藏或显示 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 CRUD Grid 2026
Student ID:
2
姓名:
王芳
课程:
Visual Basic 2026
成绩:
82
新增 刷新 修改 删除 清除
Student ID姓名课程成绩
1李明Visual Basic 202688.00
2王芳Visual Basic 202682.00
3陈伟Python Programming91.00
控件Name设置
TextBoxtxtStudentIDReadOnly = True
TextBoxtxtName学生姓名
TextBoxtxtCourse课程名称
TextBoxtxtScore成绩
ButtonbtnAdd新增
ButtonbtnRefresh刷新
ButtonbtnUpdate修改
ButtonbtnDelete删除
ButtonbtnClear清除
DataGridViewdgvStudents显示数据库记录
LabellblStatus显示状态

26.4 DataGridView 建议属性

ReadOnly
True。本课通过 TextBox 修改资料,不直接在表格内编辑。
SelectionMode
FullRowSelect。点击一个单元格时选中整行。
MultiSelect
False。一次只处理一位学生。
AllowUserToAddRows
False。新增通过 btnAdd 完成。
AllowUserToDeleteRows
False。删除通过 btnDelete 完成。
AutoSizeColumnsMode
Fill。让列自动填满表格宽度。
RowHeadersVisible
False。让界面更简洁。

26.5 什么是 DataTable?

DataTable 可以理解为内存中的表格。它包含列和行,非常适合作为 DataGridView 的数据来源。

SQL SELECT数据库查询
SqlDataAdapter执行并填充
DataTable内存表格
DataSource绑定
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
Fill() 做了什么? SqlDataAdapter 会执行 SELECT,并把返回的行与列填入 DataTable。然后只要把这个 DataTable 交给 DataGridView.DataSource,表格就能显示数据。

26.7 DataSource 数据绑定

dgvStudents.DataSource =
    table

这叫数据绑定(Data Binding)。DataGridView 根据 DataTable 的列自动产生相应的显示列。

Lesson 25

使用 SqlDataReader 逐行读取,再自己拼接到 txtOutput。

Lesson 26

使用 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
用户点击表格行
CellClick 取得 RowIndex
资料带回 TextBox

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 与手工填充

DataSource 绑定

把 DataTable 直接交给 DataGridView,代码少、列结构自动产生,适合本课。

手工 Rows.Add()

自己定义每一列和每一行,控制更细,但代码更多。复杂界面有时会使用。

这一课使用 DataTable + DataSource,是数据库数据显示最容易理解的路线。

26.20 本课的数据流

1
SELECT Students

查询数据库。

2
SqlDataAdapter.Fill

把结果填入 DataTable。

3
DataSource

把 DataTable 绑定到 dgvStudents。

4
用户选择行

CellClick 把数据带回 TextBox。

5
Update / Delete

使用选中记录的 StudentID 操作数据库。

26.21 初学者常见错误

错误 1

忘记设置 DataSource

Fill 只是把资料放入 DataTable;还要把 DataTable 指定给 dgvStudents.DataSource。

错误 2

点击表头也读取数据

CellClick 中必须先检查 e.RowIndex < 0

错误 3

更新后没有重新 LoadStudents

数据库已经改变,但表格还显示旧数据。CRUD 后应刷新。

错误 4

允许用户直接修改 StudentID

主键应来自选中记录,txtStudentID 设置 ReadOnly 更安全。

26.22 小练习

  1. 把 Score 列标题改成“成绩(%)”。
  2. 让 StudentID 列宽比 Course 列更窄。
  3. 在 lblStatus 显示当前选中学生姓名。
  4. 加入“双击行”事件,双击后弹出该学生摘要。
  5. 加入一个按钮,只重新刷新 DataGridView,不清除 TextBox。
  6. 把 DataGridView 的交替行背景效果交给 Designer 设置,观察可读性。
  7. 尝试使用 dgvStudents.CurrentRow 取得当前行,并比较与 CellClick 的差异。

本课复习

  1. DataGridView 的主要用途是什么?
  2. DataTable 是什么?
  3. SqlDataAdapter.Fill() 做什么?
  4. DataSource 的作用是什么?
  5. 为什么本课把 DataGridView 设置为 ReadOnly?
  6. FullRowSelect 有什么效果?
  7. 为什么 CellClick 要检查 e.RowIndex?
  8. 怎样把当前行的 StudentID 放到 txtStudentID?
  9. 为什么 CRUD 完成后通常要重新 LoadStudents?
  10. 为什么 txtStudentID 应该设为 ReadOnly?