DataGridView 教程:从入门到精通
DataGridView 是 Windows Forms 中功能最强大、最灵活的数据显示控件,它以表格形式展示数据,并允许用户直接进行编辑、排序和选择。

第一部分:基础入门
添加 DataGridView 到窗体
- 在 Visual Studio 中,打开或创建一个新的 Windows Forms 项目。
- 从“工具箱”(Toolbox)中,将
DataGridView控件拖放到你的窗体上。 - 你可以直接在设计时通过“智能任务”小灯泡(Smart Task)进行快速配置,或者通过代码进行完全控制。
绑定数据源
DataGridView 最常见的用法是绑定到一个数据源,数据源可以是:
- DataTable / DataSet
- BindingList (推荐用于自定义对象集合)
- List
- 数据库 (如 SQL Server, MySQL)
示例 1:绑定到 DataTable (内存数据)
这是最简单的方式,适合展示静态或内存中的数据。
using System;
using System.Data;
using System.Windows.Forms;
namespace DataGridViewTutorial
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
// 1. 创建一个 DataTable
DataTable dt = new DataTable("Products");
// 2. 添加列
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("ProductName", typeof(string));
dt.Columns.Add("Price", typeof(decimal));
dt.Columns.Add("InStock", typeof(bool));
// 3. 添加行
dt.Rows.Add(1, "笔记本电脑", 5999.99m, true);
dt.Rows.Add(2, "无线鼠标", 99.50m, true);
dt.Rows.Add(3, "机械键盘", 499.00m, false);
dt.Rows.Add(4, "4K显示器", 2999.00m, true);
// 4. 将 DataGridView 的 DataSource 属性设置为 DataTable
dataGridView1.DataSource = dt;
}
}
}
运行结果:
DataGridView 会自动根据 DataTable 的列和行生成相应的列和单元格,并自动推断列的数据类型(文本、数字、复选框等)。
第二部分:常用属性与配置
你可以通过属性窗口或代码来配置 DataGridView 的外观和行为。

| 属性名 | 描述 | 示例 |
|---|---|---|
AutoGenerateColumns |
是否根据数据源自动生成列,通常设为 true。 |
dataGridView1.AutoGenerateColumns = true; |
AllowUserToAddRows |
是否允许用户在底部添加新行。 | dataGridView1.AllowUserToAddRows = true; |
AllowUserToDeleteRows |
是否允许用户删除行(通过按 Delete 键)。 |
dataGridView1.AllowUserToDeleteRows = true; |
AllowUserToOrderColumns |
是否允许用户通过拖拽列头来重新排序列。 | dataGridView1.AllowUserToOrderColumns = true; |
ReadOnly |
整个 DataGridView 是否为只读。 |
dataGridView1.ReadOnly = false; |
SelectionMode |
用户的选择模式。FullRowSelect(选中整行)很常用。 |
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect; |
MultiSelect |
是否允许多选。 | dataGridView1.MultiSelect = false; |
BackgroundColor, GridColor |
设置背景色和网格线颜色。 | dataGridView1.GridColor = Color.LightGray; |
ColumnHeadersDefaultCellStyle |
设置列头的单元格样式(字体、颜色、对齐方式等)。 | dataGridView1.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; |
第三部分:高级操作
手动创建和配置列
自动生成的列虽然方便,但有时我们需要完全控制列的类型、顺序和属性。
场景: 我们想把 Price 列显示为货币格式,把 InStock 列显示为图像。
private void MainForm_Load(object sender, EventArgs e)
{
// ... (创建 DataTable 的代码与上面相同) ...
DataTable dt = CreateSampleDataTable();
// 1. 关闭自动生成列
dataGridView1.AutoGenerateColumns = false;
// 2. 创建并配置列
// ID 列 - 只读的文本列
DataGridViewTextBoxColumn idColumn = new DataGridViewTextBoxColumn();
idColumn.Name = "IDColumn";
idColumn.DataPropertyName = "ID"; // 绑定到 DataTable 的 "ID" 列
idColumn.HeaderText = "产品ID";
idColumn.ReadOnly = true;
// 产品名称列 - 文本列
DataGridViewTextBoxColumn nameColumn = new DataGridViewTextBoxColumn();
nameColumn.Name = "ProductNameColumn";
nameColumn.DataPropertyName = "ProductName";
nameColumn.HeaderText = "产品名称";
// 价格列 - 文本列,但我们用格式化事件来显示为货币
DataGridViewTextBoxColumn priceColumn = new DataGridViewTextBoxColumn();
priceColumn.Name = "PriceColumn";
priceColumn.DataPropertyName = "Price";
priceColumn.HeaderText = "价格 (元)";
priceColumn.DefaultCellStyle.Format = "C2"; // C2 表示货币格式,保留2位小数
// 是否有货列 - 图像列
DataGridViewImageColumn stockColumn = new DataGridViewImageColumn();
stockColumn.Name = "InStockColumn";
stockColumn.DataPropertyName = "InStock"; // 绑定到 bool 值
stockColumn.HeaderText = "是否有货";
stockColumn.ImageLayout = DataGridViewImageCellLayout.Zoom; // 图像缩放以适应单元格
// 3. 将列添加到 DataGridView
dataGridView1.Columns.AddRange(new DataGridViewColumn[] {
idColumn, nameColumn, priceColumn, stockColumn
});
// 4. 绑定数据源
dataGridView1.DataSource = dt;
}
private DataTable CreateSampleDataTable()
{
// ... (同上) ...
}
运行结果: ID 列不可编辑,价格显示为 ¥5,999.99,是否有货列根据布尔值显示一个勾选或未勾选的图标。
处理事件
事件是 DataGridView 交互的核心。

示例:处理单元格内容变更事件
当用户编辑单元格并离开时,我们可以在这里捕获新值并执行逻辑。
// 在窗体设计器中,选中 dataGridView1,在属性窗口的“闪电”图标下找到 CellValueChanged 事件,双击生成事件处理方法。
// 或者直接在代码中添加:
dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// e.RowIndex 是被修改的行索引
// e.ColumnIndex 是被修改的列索引
if (e.RowIndex >= 0)
{
// 获取被修改的单元格
DataGridViewCell cell = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
object newValue = cell.Value;
// 获取整行数据
DataGridViewRow row = dataGridView1.Rows[e.RowIndex];
int id = (int)row.Cells["IDColumn"].Value;
string productName = (string)row.Cells["ProductNameColumn"].Value;
decimal newPrice = row.Cells["PriceColumn"].Value != null ? Convert.ToDecimal(row.Cells["PriceColumn"].Value) : 0;
bool inStock = Convert.ToBoolean(row.Cells["InStockColumn"].Value);
MessageBox.Show($"产品 '{productName}' (ID: {id}) 的价格已更新为: {newPrice:C}");
}
}
示例:处理行验证事件
在用户离开编辑行之前,可以验证数据的有效性。
// 在窗体设计器中,找到 RowValidating 事件
dataGridView1.RowValidating += DataGridView1_RowValidating;
private void DataGridView1_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
{
// 验证价格不能为负数
DataGridViewRow row = dataGridView1.Rows[e.RowIndex];
decimal price;
if (decimal.TryParse(row.Cells["PriceColumn"].Value?.ToString(), out price))
{
if (price < 0)
{
MessageBox.Show("价格不能为负数!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true; // 阻止用户离开当前行
}
}
}
获取和修改数据
获取当前选中的行或单元格的值非常常见。
// 假设有一个 "Delete" 按钮
private void btnDelete_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 0)
{
// 获取用户选中的第一行
DataGridViewRow selectedRow = dataGridView1.SelectedRows[0];
// 获取该行的数据
int id = Convert.ToInt32(selectedRow.Cells["IDColumn"].Value);
string name = selectedRow.Cells["ProductNameColumn"].Value.ToString();
// 从数据源中删除 (如果你的数据源是 DataTable)
if (dataGridView1.DataSource is DataTable dt)
{
// 找到对应的行并删除
DataRow[] rowsToDelete = dt.Select($"ID = {id}");
if (rowsToDelete.Length > 0)
{
dt.Rows.Remove(rowsToDelete[0]);
// dt.AcceptChanges(); // 如果需要提交更改
}
}
// 或者直接从 DataGridView 中移除行
// dataGridView1.Rows.Remove(selectedRow);
}
else
{
MessageBox.Show("请先选择一行要删除的数据。");
}
}
第四部分:与数据库交互
这是 DataGridView 最强大的应用场景,我们将使用 SqlDataAdapter 从 SQL Server 数据库填充 DataTable。
准备工作:
- 在 SQL Server 中创建一个数据库和一张表,
Products。 - 插入一些示例数据。
CREATE DATABASE MyShop;
GO
USE MyShop;
GO
CREATE TABLE Products (
ID INT PRIMARY KEY,
ProductName NVARCHAR(100),
Price DECIMAL(10, 2),
InStock BIT
);
GO
INSERT INTO Products VALUES (1, '笔记本电脑', 5999.99, 1);
INSERT INTO Products VALUES (2, '无线鼠标', 99.50, 1);
INSERT INTO Products VALUES (3, '机械键盘', 499.00, 0);
C# 代码:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
namespace DataGridViewDatabaseTutorial
{
public partial class MainForm : Form
{
private string connectionString = "Server=.;Database=MyShop;Integrated Security=True;";
public MainForm()
{
InitializeComponent();
this.Load += MainForm_Load;
}
private void MainForm_Load(object sender, EventArgs e)
{
LoadData();
}
private void LoadData()
{
try
{
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Products", con);
DataTable dt = new DataTable();
adapter.Fill(dt);
dataGridView1.DataSource = dt;
}
}
catch (Exception ex)
{
MessageBox.Show("加载数据失败: " + ex.Message);
}
}
// 假设有一个 "Save" 按钮,用于将 DataGridView 的更改保存回数据库
private void btnSave_Click(object sender, EventArgs e)
{
try
{
using (SqlConnection con = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Products", con);
// 创建一个命令构建器,它会自动生成 INSERT, UPDATE, DELETE 命令
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
// 获取 DataTable
DataTable dt = (DataTable)dataGridView1.DataSource;
// 更新数据库
adapter.Update(dt);
MessageBox.Show("数据已成功保存到数据库!");
}
}
catch (Exception ex)
{
MessageBox.Show("保存数据失败: " + ex.Message);
}
}
}
}
说明:
SqlDataAdapter负责从数据库查询数据并填充到DataTable。SqlCommandBuilder是一个非常方便的工具,它会根据SelectCommand自动生成InsertCommand,UpdateCommand,DeleteCommand。- 调用
adapter.Update(dt)时,它会检查DataTable的RowState(Added,Modified,Deleted),并执行相应的数据库操作。
第五部分:性能优化与最佳实践
当处理大量数据(例如超过 10,000 行)时,DataGridView 的性能可能会下降。
-
使用
BindingList<T>而不是List<T>:- 如果你使用自定义对象的集合,
BindingList<T>提供了ListChanged事件,当数据源发生变化时,DataGridView会自动更新,数据同步更高效。
public class Product { public int ID { get; set; } public string ProductName { get; set; } public decimal Price { get; set; } public bool InStock { get; set; } } // 在窗体加载时 List<Product> productList = new List<Product>(); // ... 填充 productList ... BindingList<Product> bindingList = new BindingList<Product>(productList); dataGridView1.DataSource = bindingList; - 如果你使用自定义对象的集合,
-
虚拟模式:
- 对于海量数据,不要一次性加载所有数据,虚拟模式允许你只加载当前可见的行,并在用户滚动时按需加载。
- 这需要实现
DataGridView.VirtualMode = true;并处理CellValueNeeded事件,在需要时提供单元格的值。
-
避免在设计时绑定复杂数据源:
- 对于数据库查询等耗时操作,最好在代码中(如
Form_Load事件)进行数据绑定,而不是在设计器中直接设置DataSource,以保持界面的响应性。
- 对于数据库查询等耗时操作,最好在代码中(如
-
冻结列:
- 当列很多时,可以冻结重要的列,使其在水平滚动时保持可见。
- 在设计器中选中列,设置
Frozen属性为True。
DataGridView 是一个极其灵活和强大的控件,通过本教程,你应该已经掌握了:
- 基础绑定:如何将
DataTable或List<T>绑定到控件。 - 自定义列:如何手动创建和配置列的类型和样式。
- 事件处理:如何响应用户的操作,如编辑、验证。
- 数据操作:如何获取和修改显示的数据。
- 数据库集成:如何从数据库加载数据并保存更改。
- 性能考量:了解处理大数据时的优化策略。
从简单的列表显示到复杂的数据管理,DataGridView 都能胜任,不断练习和探索其更多高级特性,你将能构建出功能完善且用户体验良好的数据驱动的应用程序。
