杰瑞科技汇

datagridview 教程

DataGridView 教程:从入门到精通

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

datagridview 教程-图1
(图片来源网络,侵删)

第一部分:基础入门

添加 DataGridView 到窗体

  1. 在 Visual Studio 中,打开或创建一个新的 Windows Forms 项目。
  2. 从“工具箱”(Toolbox)中,将 DataGridView 控件拖放到你的窗体上。
  3. 你可以直接在设计时通过“智能任务”小灯泡(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 的外观和行为。

datagridview 教程-图2
(图片来源网络,侵删)
属性名 描述 示例
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 交互的核心。

datagridview 教程-图3
(图片来源网络,侵删)

示例:处理单元格内容变更事件

当用户编辑单元格并离开时,我们可以在这里捕获新值并执行逻辑。

// 在窗体设计器中,选中 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

准备工作:

  1. 在 SQL Server 中创建一个数据库和一张表,Products
  2. 插入一些示例数据。
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) 时,它会检查 DataTableRowStateAdded, Modified, Deleted),并执行相应的数据库操作。

第五部分:性能优化与最佳实践

当处理大量数据(例如超过 10,000 行)时,DataGridView 的性能可能会下降。

  1. 使用 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;
  2. 虚拟模式

    • 对于海量数据,不要一次性加载所有数据,虚拟模式允许你只加载当前可见的行,并在用户滚动时按需加载。
    • 这需要实现 DataGridView.VirtualMode = true; 并处理 CellValueNeeded 事件,在需要时提供单元格的值。
  3. 避免在设计时绑定复杂数据源

    • 对于数据库查询等耗时操作,最好在代码中(如 Form_Load 事件)进行数据绑定,而不是在设计器中直接设置 DataSource,以保持界面的响应性。
  4. 冻结列

    • 当列很多时,可以冻结重要的列,使其在水平滚动时保持可见。
    • 在设计器中选中列,设置 Frozen 属性为 True

DataGridView 是一个极其灵活和强大的控件,通过本教程,你应该已经掌握了:

  • 基础绑定:如何将 DataTableList<T> 绑定到控件。
  • 自定义列:如何手动创建和配置列的类型和样式。
  • 事件处理:如何响应用户的操作,如编辑、验证。
  • 数据操作:如何获取和修改显示的数据。
  • 数据库集成:如何从数据库加载数据并保存更改。
  • 性能考量:了解处理大数据时的优化策略。

从简单的列表显示到复杂的数据管理,DataGridView 都能胜任,不断练习和探索其更多高级特性,你将能构建出功能完善且用户体验良好的数据驱动的应用程序。

分享:
扫描分享到社交APP
上一篇
下一篇