当前位置: 首页 > news >正文

C# WinForm DataGridView数据绑定与CRUD操作实战教程

1. 项目缘起:为什么DataGridView是WinForm开发的“定海神针”?

如果你正在用C#开发Windows桌面应用,尤其是那些需要展示、编辑、管理表格数据的应用,比如库存管理系统、客户信息表、订单列表或者日志查看器,那么你几乎绕不开一个控件:DataGridView。它不是最炫酷的,但绝对是使用频率最高、功能最强大的数据展示控件之一。很多新手在初次接触时,可能会被它复杂的属性、事件和方法吓到,觉得它“又大又笨重”。但当你真正掌握其核心用法后,你会发现它就像一个瑞士军刀,能解决你80%以上的表格数据交互需求。今天,我们就从一个最基础、最实用的角度切入,通过一个完整的“员工信息管理”实例,手把手带你搞懂DataGridView的核心三板斧:数据绑定、样式定制和事件处理。我们不会一开始就钻进那些高级的“虚拟模式”或“自定义单元格绘制”,而是先把地基打牢,让你能快速做出一个功能完整、界面美观的数据表格应用。相信我,理解了这些基础,再去探索高级特性,你会事半功倍。

2. 环境准备与项目搭建:从零创建一个WinForm应用

在开始编码之前,我们得先把“厨房”收拾好。这里我们使用最经典的开发环境:Visual Studio 2022 和 .NET Framework 4.8(当然,.NET 6/8 的 WinForms 项目也完全适用,核心API几乎一致)。

2.1 创建项目与界面布局

首先,打开Visual Studio,创建一个新的“Windows窗体应用(.NET Framework)”项目,命名为EmployeeManager

  1. 设计主窗体:打开默认的Form1,将它的Text属性修改为“员工信息管理”。
  2. 拖放控件:从工具箱的“所有Windows窗体”或“公共控件”分组中,找到DataGridView控件,将其拖拽到窗体上。调整其Dock属性为Fill,让它铺满整个窗体客户区。
  3. 添加操作按钮:在工具箱中找到Button控件,在窗体的顶部或底部(可以通过一个Panel容器来布局)添加四个按钮,分别命名为:btnAdd,btnEdit,btnDelete,btnRefresh。它们的Text属性分别设置为“新增(&A)”、“编辑(&E)”、“删除(&D)”、“刷新(&R)”。这里的&符号用于定义快捷键(Alt+A等)。
  4. 最终布局预览:一个简单的界面就出来了:顶部是一排操作按钮,下方是一个占据主要区域的DataGridView,用于展示员工列表。

提示:在拖放DataGridView后,右上角会出现一个智能标记(小箭头),点击它可以快速进行一些常用操作,如选择数据源、启用添加/编辑/删除等。但我们为了彻底理解原理,先不通过这个向导,而是全部通过代码来实现。

2.2 定义数据模型:员工类 (Employee)

任何数据绑定的核心都是数据模型。我们先创建一个简单的类来表示员工信息。在项目中右键,选择“添加” -> “类”,命名为Employee.cs

namespace EmployeeManager { public class Employee { public int Id { get; set; } // 员工ID public string Name { get; set; } // 姓名 public string Department { get; set; } // 部门 public string Position { get; set; } // 职位 public DateTime HireDate { get; set; } // 入职日期 public decimal Salary { get; set; } // 薪资 // 为了方便演示,提供一个生成示例数据的方法 public static List<Employee> GetSampleData() { return new List<Employee> { new Employee { Id = 1, Name = "张三", Department = "技术部", Position = "高级工程师", HireDate = new DateTime(2020, 5, 10), Salary = 25000 }, new Employee { Id = 2, Name = "李四", Department = "市场部", Position = "经理", HireDate = new DateTime(2019, 8, 22), Salary = 18000 }, new Employee { Id = 3, Name = "王五", Department = "财务部", Position = "会计", HireDate = new DateTime(2021, 3, 15), Salary = 8000 }, new Employee { Id = 4, Name = "赵六", Department = "技术部", Position = "实习生", HireDate = new DateTime(2023, 7, 1), Salary = 4000 }, new Employee { Id = 5, Name = "钱七", Department = "人事部", Position = "专员", HireDate = new DateTime(2022, 11, 30), Salary = 7500 } }; } } }

这个Employee类就是我们的数据实体。GetSampleData方法提供了一个内存中的模拟数据源,在实际项目中,这里应该替换为从数据库(如SQL Server, MySQL)或Web API获取数据的逻辑。

3. 核心绑定:将数据“塞进”DataGridView的三种姿势

数据有了,界面有了,现在如何把它们连接起来?这就是数据绑定。DataGridView支持多种数据源,我们主要讲最常用的两种:List<T>DataTable

3.1 方式一:绑定List (最推荐的方式)

这是面向对象编程中最自然的方式。我们直接在窗体的加载事件中,将Employee列表绑定到DataGridView

首先,在Form1类中声明一个成员变量来持有数据:

private List<Employee> employeeList;

然后,在Form1_Load事件处理方法中编写代码:

private void Form1_Load(object sender, EventArgs e) { // 1. 获取数据 employeeList = Employee.GetSampleData(); // 2. 设置数据源 dataGridView1.DataSource = employeeList; // 3. (可选)隐藏自动生成的ID列,通常ID不需要展示给用户 dataGridView1.Columns["Id"].Visible = false; }

就这么简单!运行程序,你会看到表格自动生成了与Employee类属性对应的列,并填满了数据。DataGridView通过反射读取了类的公共属性,并自动创建了列。这种方式的好处是强类型,编译时就能发现属性名错误,并且便于后续对列表进行增删改查操作。

3.2 方式二:绑定DataTable(传统ADO.NET风格)

如果你处理的是从数据库直接查询出来的DataTable,或者需要更动态的列结构,也可以直接绑定DataTable

private void BindDataTable() { DataTable dt = new DataTable("Employees"); // 定义列 dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("Name", typeof(string)); dt.Columns.Add("Department", typeof(string)); // ... 添加其他列 // 填充行数据 dt.Rows.Add(1, "张三", "技术部"); dt.Rows.Add(2, "李四", "市场部"); // ... 添加其他行 dataGridView1.DataSource = dt; }

这种方式更灵活,列可以动态添加删除,但失去了强类型的好处,容易因列名拼写错误导致运行时异常。

3.3 手动创建列(精细化控制)

自动生成的列虽然方便,但往往不符合我们的UI要求。比如,我们想把“HireDate”显示为“入职日期”,把“Salary”格式化为货币并右对齐。这时就需要手动创建列。

我们可以在绑定数据源之前,先配置好DataGridView的列。将Form1_Load中的代码修改如下:

private void Form1_Load(object sender, EventArgs e) { // 先配置列,再绑定数据源 ConfigureDataGridViewColumns(); employeeList = Employee.GetSampleData(); dataGridView1.DataSource = employeeList; } private void ConfigureDataGridViewColumns() { // 清除可能存在的自动生成列 dataGridView1.AutoGenerateColumns = false; // 手动添加列 // 文本列 - 姓名 DataGridViewTextBoxColumn colName = new DataGridViewTextBoxColumn(); colName.Name = "colName"; // 列在代码中的标识 colName.HeaderText = "姓名"; // 列标题显示的文字 colName.DataPropertyName = "Name"; // 绑定的数据源属性名 colName.Width = 100; dataGridView1.Columns.Add(colName); // 文本列 - 部门 DataGridViewTextBoxColumn colDept = new DataGridViewTextBoxColumn(); colDept.Name = "colDept"; colDept.HeaderText = "部门"; colDept.DataPropertyName = "Department"; colDept.Width = 120; dataGridView1.Columns.Add(colDept); // 文本列 - 职位 DataGridViewTextBoxColumn colPos = new DataGridViewTextBoxColumn(); colPos.Name = "colPos"; colPos.HeaderText = "职位"; colPos.DataPropertyName = "Position"; colPos.Width = 120; dataGridView1.Columns.Add(colPos); // 日期列 - 入职日期 DataGridViewTextBoxColumn colHireDate = new DataGridViewTextBoxColumn(); // 仍用TextBox列,但设置格式 colHireDate.Name = "colHireDate"; colHireDate.HeaderText = "入职日期"; colHireDate.DataPropertyName = "HireDate"; colHireDate.Width = 100; colHireDate.DefaultCellStyle.Format = "yyyy-MM-dd"; // 设置日期格式 colHireDate.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; dataGridView1.Columns.Add(colHireDate); // 文本列 - 薪资(货币格式) DataGridViewTextBoxColumn colSalary = new DataGridViewTextBoxColumn(); colSalary.Name = "colSalary"; colSalary.HeaderText = "月薪"; colSalary.DataPropertyName = "Salary"; colSalary.Width = 100; colSalary.DefaultCellStyle.Format = "C2"; // “C2”表示货币格式,保留两位小数 colSalary.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; dataGridView1.Columns.Add(colSalary); // 按钮列 - 操作(如查看详情) DataGridViewButtonColumn colAction = new DataGridViewButtonColumn(); colAction.Name = "colAction"; colAction.HeaderText = "操作"; colAction.Text = "详情"; colAction.UseColumnTextForButtonValue = true; // 按钮显示统一的文本 colAction.Width = 80; dataGridView1.Columns.Add(colAction); }

关键点在于AutoGenerateColumns = falseDataPropertyName属性。前者告诉控件不要自动创建列,后者指定了该列绑定到数据源的哪个属性。通过手动创建列,我们获得了对列标题、宽度、数据格式、对齐方式等的完全控制权。我们还添加了一个按钮列,这为后续处理行内操作事件打下了基础。

4. 样式美化与用户体验提升

一个光秃秃的表格是缺乏美感和可用性的。DataGridView提供了丰富的样式属性,可以轻松美化。

4.1 设置交替行背景色(斑马线效果)

让奇偶行显示不同的背景色,可以极大提高长表格的可读性。

private void ApplyZebraStriping() { dataGridView1.AlternatingRowsDefaultCellStyle.BackColor = Color.LightCyan; // 你也可以设置非交替行的默认背景色 // dataGridView1.RowsDefaultCellStyle.BackColor = Color.White; }

4.2 设置列头与网格线样式

private void ApplyGridStyles() { // 1. 列头样式 dataGridView1.ColumnHeadersDefaultCellStyle.BackColor = Color.Navy; dataGridView1.ColumnHeadersDefaultCellStyle.ForeColor = Color.White; dataGridView1.ColumnHeadersDefaultCellStyle.Font = new Font("微软雅黑", 10, FontStyle.Bold); dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing; dataGridView1.ColumnHeadersHeight = 40; // 设置列头高度 // 2. 网格线颜色 dataGridView1.GridColor = Color.LightGray; // 3. 边框样式 dataGridView1.BorderStyle = BorderStyle.Fixed3D; // 4. 启用行头(最左侧的灰色区域),但可以隐藏 dataGridView1.RowHeadersVisible = false; // 5. 允许用户调整列宽和行高 dataGridView1.AllowUserToResizeColumns = true; dataGridView1.AllowUserToResizeRows = false; // 通常行高不需要调整 // 6. 选择模式:整行选择 dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect; dataGridView1.MultiSelect = false; // 禁止多选,简化操作 }

4.3 条件格式:高亮特定数据

我们可以根据单元格的值来动态设置其样式。例如,高亮显示薪资高于20000的员工。

private void ApplyConditionalFormatting() { // 使用CellFormatting事件动态设置样式 dataGridView1.CellFormatting += (sender, e) => { if (e.RowIndex >= 0 && e.ColumnIndex >= 0) // 确保是数据行,不是列头或行头 { DataGridView dgv = sender as DataGridView; if (dgv.Columns[e.ColumnIndex].DataPropertyName == "Salary") // 只处理薪资列 { if (dgv.Rows[e.RowIndex].DataBoundItem is Employee emp) { if (emp.Salary > 20000m) { e.CellStyle.BackColor = Color.LightGoldenrodYellow; e.CellStyle.Font = new Font(e.CellStyle.Font, FontStyle.Bold); e.CellStyle.ForeColor = Color.DarkRed; } } } } }; }

CellFormatting事件在单元格即将被绘制时触发,是进行条件格式化的绝佳位置。注意,这里我们通过DataBoundItem获取到了绑定到该行的原始Employee对象,这是一种安全且强类型的访问方式。

5. 实现增删改查:让表格“活”起来

静态的表格没有灵魂。现在,我们为之前添加的四个按钮实现功能,并与DataGridView联动。

5.1 刷新数据

这是最简单的。刷新按钮通常用于重新从数据源(数据库)加载数据。

private void btnRefresh_Click(object sender, EventArgs e) { // 模拟从数据库重新加载 employeeList = Employee.GetSampleData(); // 实际项目中,这里应是数据库查询 dataGridView1.DataSource = null; // 先清空现有绑定 dataGridView1.DataSource = employeeList; // 重新绑定 // 如果手动配置了列,记得重新应用列配置或保持AutoGenerateColumns=false }

5.2 新增与编辑数据

新增和编辑通常通过一个独立的“编辑窗体”来完成。我们先创建一个新的窗体FormEmployeeEdit

  1. 创建编辑窗体:添加新窗体,设计界面,包含对应Employee属性的TextBoxComboBox等控件,以及“确定”、“取消”按钮。
  2. 在Form1中实现新增逻辑
private void btnAdd_Click(object sender, EventArgs e) { using (FormEmployeeEdit editForm = new FormEmployeeEdit()) { editForm.Text = "新增员工"; if (editForm.ShowDialog() == DialogResult.OK) { Employee newEmp = editForm.GetEmployee(); // 假设编辑窗体有一个方法返回编辑后的Employee对象 newEmp.Id = employeeList.Max(e => e.Id) + 1; // 生成新ID(模拟,实际应由数据库生成) employeeList.Add(newEmp); // 刷新DataGridView绑定 // 方法1:重新绑定整个列表(简单但效率略低) dataGridView1.DataSource = null; dataGridView1.DataSource = employeeList; // 方法2:如果DataGridView绑定的是BindingSource,可以调用ResetBindings // bindingSource.ResetBindings(false); } } }
  1. 在Form1中实现编辑逻辑

编辑需要获取当前选中的行。

private void btnEdit_Click(object sender, EventArgs e) { if (dataGridView1.CurrentRow == null || dataGridView1.CurrentRow.Index < 0) { MessageBox.Show("请先选择要编辑的员工行。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } // 获取选中行绑定的数据对象 Employee selectedEmp = dataGridView1.CurrentRow.DataBoundItem as Employee; if (selectedEmp == null) return; using (FormEmployeeEdit editForm = new FormEmployeeEdit(selectedEmp)) // 假设构造函数接收一个Employee对象用于初始化 { editForm.Text = "编辑员工信息"; if (editForm.ShowDialog() == DialogResult.OK) { Employee updatedEmp = editForm.GetEmployee(); // 更新列表中的对象(因为是引用类型,selectedEmp已被编辑窗体修改,这里通常只需刷新显示) // 如果编辑窗体返回的是新对象,则需要替换原对象 // int index = employeeList.IndexOf(selectedEmp); // employeeList[index] = updatedEmp; // 强制刷新当前行的显示 dataGridView1.InvalidateRow(dataGridView1.CurrentRow.Index); } } }

这里的关键是dataGridView1.CurrentRow.DataBoundItem,它直接拿到了绑定到该行的原始数据对象。修改这个对象,DataGridView中显示的数据并不会自动更新,因为绑定的是对象的引用,对象的属性值变了,但引用没变。我们需要调用InvalidateRow来触发重绘,或者通过BindingSource的机制通知界面更新。

5.3 删除数据

删除操作需要谨慎,通常需要确认。

private void btnDelete_Click(object sender, EventArgs e) { if (dataGridView1.CurrentRow == null) { MessageBox.Show("请先选择要删除的员工行。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } Employee empToDelete = dataGridView1.CurrentRow.DataBoundItem as Employee; if (empToDelete == null) return; DialogResult result = MessageBox.Show($"确定要删除员工【{empToDelete.Name}】吗?", "确认删除", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result == DialogResult.Yes) { employeeList.Remove(empToDelete); // 由于我们绑定的是List,直接移除对象后,需要重置绑定源来更新UI // 简单做法:重新绑定 dataGridView1.DataSource = null; dataGridView1.DataSource = employeeList; // 更优做法:使用BindingSource,调用RemoveCurrent() } }

直接操作List后,DataGridView不会自动感知变化。重置DataSource是一种方法,但在数据量大时效率不高。最佳实践是使用BindingSource组件。

5.4 使用BindingSource进行优雅的数据绑定

BindingSource是一个中间层,它封装了数据源,并提供了更强大的变更通知、排序、过滤和导航功能。

  1. 从工具箱拖一个BindingSource组件到窗体上,默认名称为bindingSource1
  2. 修改绑定代码
private BindingSource bindingSource1 = new BindingSource(); // 如果在设计器拖放,会自动生成 private void Form1_Load(object sender, EventArgs e) { ConfigureDataGridViewColumns(); employeeList = Employee.GetSampleData(); // 将List绑定到BindingSource bindingSource1.DataSource = employeeList; // 将DataGridView绑定到BindingSource dataGridView1.DataSource = bindingSource1; }
  1. 修改增删改查代码
    • 新增bindingSource1.Add(newEmp);
    • 编辑:修改对象属性后,调用bindingSource1.ResetCurrentItem();
    • 删除bindingSource1.RemoveCurrent();

使用BindingSource后,所有对数据的增删改操作都会自动同步反映到DataGridView上,无需手动重置DataSource,代码更简洁,性能也更好。

6. 处理用户交互:事件驱动编程

DataGridView提供了大量事件,让我们能够响应用户的点击、编辑、选择等操作。

6.1 处理单元格点击与按钮列点击

我们之前添加了一个“详情”按钮列。如何响应它的点击事件?

private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) { // CellContentClick事件在单元格内的内容(如按钮)被点击时触发 DataGridView dgv = sender as DataGridView; // 确保点击的是有效的单元格,并且是我们定义的按钮列 if (e.RowIndex >= 0 && e.ColumnIndex >= 0 && dgv.Columns[e.ColumnIndex] is DataGridViewButtonColumn) { // 获取对应行的数据 Employee emp = dgv.Rows[e.RowIndex].DataBoundItem as Employee; if (emp != null) { MessageBox.Show($"员工详情:\n姓名:{emp.Name}\n部门:{emp.Department}\n职位:{emp.Position}\n入职日期:{emp.HireDate:yyyy-MM-dd}\n薪资:{emp.Salary:C2}", "员工信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } } }

记得在设计器中将dataGridView1CellContentClick事件与这个方法关联起来。

6.2 处理单元格值变更(编辑后)

当用户在DataGridView中直接编辑单元格时,我们需要捕获这个变化并更新底层数据。

private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e) { // 当单元格的值被用户改变并提交后触发(例如,编辑完离开单元格) if (e.RowIndex >= 0 && e.ColumnIndex >= 0) { DataGridView dgv = sender as DataGridView; // 注意:此时DataBoundItem可能已经是最新值,因为绑定是双向的(如果属性实现了INotifyPropertyChanged则更好)。 // 但为了保险,我们可以显式更新。 // 如果绑定的是BindingSource,通常会自动处理。 // 这里可以记录日志或进行数据验证。 Console.WriteLine($"单元格 [{e.RowIndex}, {e.ColumnIndex}] 的值已更改。"); } }

注意:如果希望底层数据对象(如Employee)的属性变更能自动通知UI更新,需要让Employee类实现INotifyPropertyChanged接口。这对于BindingSource和MVVM模式非常重要。

6.3 处理行选择变化

当用户点击不同的行时,我们可能需要在界面的其他部分(如一个详细信息面板)同步显示当前选中行的内容。

private void dataGridView1_SelectionChanged(object sender, EventArgs e) { if (dataGridView1.CurrentRow != null && dataGridView1.CurrentRow.DataBoundItem is Employee selectedEmp) { // 更新其他控件的显示,例如几个Label或TextBox // lblName.Text = selectedEmp.Name; // lblDept.Text = selectedEmp.Department; // ... } else { // 清空其他控件的显示 } }

7. 常见问题与避坑指南

在实际使用中,你肯定会遇到一些“坑”。这里分享几个最常见的。

7.1 数据绑定后,新增/删除行界面不更新

问题:直接对List<T>进行AddRemove操作后,DataGridView没有变化。根因List<T>没有实现IBindingList接口,因此不提供列表变更通知。解决方案

  1. 使用BindingSource:如前所述,这是最推荐的方式。将List<T>赋值给BindingSource.DataSource,然后通过BindingSource的方法(Add,Remove,RemoveCurrent)来操作数据。
  2. 使用ObservableCollection<T>(WPF/Silverlight风格,WinForms中需额外引用):它实现了INotifyCollectionChanged,能自动通知UI。但在WinForms中,仍需通过BindingSource或自定义绑定逻辑来利用这一特性,不如直接使用BindingSource方便。
  3. 重置DataSource:每次操作后,执行dataGridView1.DataSource = null; dataGridView1.DataSource = myList;。简单粗暴,但数据量大时性能差,且会丢失排序、筛选状态和当前选择。

7.2 单元格编辑后,数据没有提交到数据源

问题:用户在单元格里输入了内容,但离开单元格后,绑定的List<T>DataTable里的值没变。根因:编辑的内容还在DataGridView的缓存中,没有“提交”到绑定的数据源。提交通常发生在行焦点离开或调用EndEdit时。解决方案

  • 在获取数据前,强制结束当前编辑:dataGridView1.EndEdit();
  • 确保数据对象的属性有setter
  • 对于DataTable,编辑会自动提交。对于对象绑定,需要触发CellValueChanged事件或手动结束编辑。

7.3 性能问题:加载大量数据时卡顿

问题:绑定成千上万行数据时,界面加载慢,滚动卡顿。根因DataGridView默认会为每一行数据创建一个DataGridViewRow对象及其单元格,非常消耗资源。解决方案

  1. 分页加载:这是最有效的方案。只加载当前页的数据。
  2. 启用虚拟模式:设置dataGridView1.VirtualMode = true;。你需要自己实现CellValueNeeded等事件来按需提供数据。这需要较高的编程技巧,但能处理海量数据。
  3. 关闭自动调整:在绑定数据前,设置dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;并手动设置合理的列宽。避免绑定后控件自动计算所有列宽和行高。
  4. 使用双缓冲dataGridView1.DoubleBuffered = true;(需要通过反射设置,因为该属性是protected的)。这能减少滚动时的闪烁。
  5. 批量操作时暂停绘制
    dataGridView1.SuspendLayout(); // ... 进行大量的添加行、修改列等操作 dataGridView1.ResumeLayout();

7.4 如何获取选中行的原始数据对象

这是最常被问到的问题之一。我们已经用到了正确的方法:

// 获取当前选中行 DataGridViewRow selectedRow = dataGridView1.CurrentRow; if (selectedRow != null && selectedRow.DataBoundItem != null) { Employee emp = selectedRow.DataBoundItem as Employee; // 使用 emp... } // 获取所有选中行(当MultiSelect=true时) foreach (DataGridViewRow row in dataGridView1.SelectedRows) { if (row.DataBoundItem is Employee emp) { // ... } }

绝对不要通过遍历单元格文本 (row.Cells["Name"].Value.ToString()) 来重新构造对象,这既低效又容易出错。

7.5 合并单元格、自定义单元格绘制等高级需求

DataGridView本身不支持直接的单元格合并。实现合并通常需要:

  1. 重写CellPainting事件,自己绘制背景和边框,模拟合并效果。
  2. 或者,使用第三方开源控件库(如DevExpress,Telerik,Syncfusion的网格控件),它们内置了合并功能。

自定义单元格绘制(例如,在单元格内画进度条、图表)也是通过CellPainting事件来实现的,这需要一定的GDI+绘图知识。

8. 项目实例完整串联与扩展思考

让我们把上面的知识点串联起来,形成一个可运行的迷你员工管理系统。核心步骤回顾:

  1. 建项目,拉控件:创建WinForm项目,放置DataGridView和按钮。
  2. 定模型:创建Employee类。
  3. 配列:在窗体加载时,关闭自动生成列,手动创建并配置好每一列(标题、宽度、格式)。
  4. 绑数据:创建BindingSource,将Employee列表绑定到它,再将BindingSource绑定到DataGridView
  5. 写样式:设置交替行颜色、列头样式、选择模式等。
  6. 做功能:为四个按钮编写事件处理程序,通过操作BindingSourceList<T>来实现增删改查,并通过FormEmployeeEdit对话框进行数据编辑。
  7. 加交互:处理DataGridViewCellContentClick事件响应按钮列,处理SelectionChanged事件更新状态。

扩展思考

  • 数据持久化:将employeeList替换为真正的数据库访问(如使用Entity Framework CoreDapper)。按钮的点击事件中,操作将变为对数据库的增删改查,然后重新绑定到DataGridView
  • 排序与过滤BindingSource天然支持排序 (Sort属性) 和过滤 (Filter属性)。你可以轻松实现点击列头排序,或添加一个文本框实现实时过滤。
  • 导出数据:可以添加功能,将DataGridView中的数据导出为 Excel (EPPlus库)、CSV 或 PDF 文件。
  • 分页:当数据量很大时,实现一个分页控件,结合数据库查询的SKIPTAKE语句,每次只加载一页数据到DataGridView

DataGridView是一个深度非常深的控件,本文涵盖的只是其基础应用的核心部分。但掌握了数据绑定、样式配置、事件处理和BindingSource的使用,你已经能够应对绝大多数常见的业务表格开发需求。记住,遇到复杂需求时,多查官方文档,善用智能感知,并理解其“数据绑定”的核心思想,很多问题都会迎刃而解。

http://www.jsqmd.com/news/1286028/

相关文章:

  • STM32 FMC/FSMC外部存储器配置详解与HAL库实战避坑指南
  • 物联网设备电池寿命优化:NBM7100A与dsPIC33EP方案
  • Cadence Allegro 17.4 Snap Pick To功能详解:从原理到高效布线实战
  • 2026年场景化盘点:付金龙运营全屏通智能眼镜靠谱吗?这份严选指南给你答案 - geo交流
  • 2026 年 7 月新发布:甘孜优秀的穿孔吸音硅酸钙板加工厂选型指南,你家影院声学效果差?居然不是靠音箱,是这玩意儿在发力? - 行业严选官
  • 原生IP与广播IP:代理IP选择必看指南
  • 2026广州瓷砖空鼓松动修缮指南/地砖墙砖微创注浆修复 本地家装维保科普 - 宅安选房屋修缮
  • 【北方生物质取暖炉炉膛结焦堵塞成因与养殖 / 厂房落地防积焦实操方案】
  • Verilog手撕代码:从数字“1”到电路直觉的构建
  • 广州汽车配件改装用品展亮点全汇总 - 服务品牌热点
  • C++浮点数格式化输出:从基础原理到高精度控制
  • 审视所有大型语言模型的架构边界-九大弱点阻碍AGI
  • 中年女性在深圳龙华找兼职
  • VMware虚拟机网络配置指南:从NAT到桥接,实现虚拟机间稳定通信
  • Unity渲染管线自动转换:HDRP与URP项目迁移实战指南
  • 广州人工智能应用工程师报名前必看:入口、条件、考试一次说清 - 人工智能报名机构推荐
  • 从静态代码到智能自治:智能合约 2.0 技术架构、创新能力与实体落地全解
  • BERT模型Embedding层解析与应用优化
  • GEO如何抢占AI搜索流量
  • 2026年甄选平阴沧州博汇管道源头厂家的4个关键维度 - geo交流
  • 匿名管道--任务派发程序
  • B站字幕提取全攻略:从手动抓包到Python自动化脚本实现
  • agent多层调用
  • 江门漏水检测正规公司推荐-同城防水补漏免砸砖维修-暗管漏水精准定位-卫生间-屋顶-阳台-厨房-地下室渗漏综合治理指南 - 知途管道科技
  • 技术人做专利转让,我是怎么选平台的?
  • 2026年AI技术老师挑选全指南:4步筛选法帮你选到靠谱落地导师
  • DSTE咨询洞察:读懂华为BLM底层逻辑:别再只把它当战略模板套用
  • DIY蓝牙公仔音响:从模块拆解到手工缝合的完整制作指南
  • Arduino/ESP32智能小车摇头避障:从硬件选型到算法优化的完整指南
  • 医院在开展医学新技术转化时,合规的知识产权评估和授权步骤有哪些?