Winform DataGridView 5个高级封装方法实战分页、样式、CRUD操作封装在C# WinForms开发中DataGridView是最常用的数据展示控件之一。然而直接使用原生功能往往会导致代码重复、维护困难。本文将分享5个经过实战检验的高级封装方法帮助开发者构建可复用的DataGridView辅助类库。1. 数据填充与样式统一封装数据绑定是DataGridView最基础的功能但每次都要重复设置样式和列属性。下面这个封装方法可以一键完成数据绑定和样式配置public static void BindDataT(DataGridView dgv, ListT data, Dictionarystring, string columnMappings null) { dgv.AutoGenerateColumns false; dgv.AllowUserToAddRows false; dgv.RowHeadersVisible false; dgv.DefaultCellStyle.Font new Font(微软雅黑, 10); // 自动生成列或使用自定义映射 if (columnMappings null) { dgv.AutoGenerateColumns true; } else { dgv.Columns.Clear(); foreach (var mapping in columnMappings) { dgv.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName mapping.Key, HeaderText mapping.Value, AutoSizeMode DataGridViewAutoSizeColumnMode.Fill }); } } dgv.DataSource new BindingListT(data); // 统一设置表头样式 dgv.ColumnHeadersDefaultCellStyle new DataGridViewCellStyle { BackColor Color.SteelBlue, ForeColor Color.White, Font new Font(微软雅黑, 11, FontStyle.Bold), Alignment DataGridViewContentAlignment.MiddleCenter }; }使用示例var employees GetEmployees(); // 获取数据 var columnMappings new Dictionarystring, string { { Id, 员工ID }, { Name, 姓名 }, { Department, 部门 } }; DataGridViewHelper.BindData(dataGridView1, employees, columnMappings);2. 智能分页封装实现分页是数据展示的常见需求以下封装实现了自动计算页数、页码跳转等功能public class PagedResultT { public ListT Data { get; set; } public int TotalCount { get; set; } public int PageSize { get; set; } public int CurrentPage { get; set; } } public static PagedResultT GetPagedDataT(ListT data, int pageSize, int currentPage) { return new PagedResultT { Data data.Skip((currentPage - 1) * pageSize).Take(pageSize).ToList(), TotalCount data.Count, PageSize pageSize, CurrentPage currentPage }; } public static void BindPagedDataT(DataGridView dgv, PagedResultT pagedResult, Label lblPageInfo, Button btnPrev, Button btnNext) { dgv.DataSource pagedResult.Data; int totalPages (int)Math.Ceiling((double)pagedResult.TotalCount / pagedResult.PageSize); lblPageInfo.Text $第{pagedResult.CurrentPage}页/共{totalPages}页; btnPrev.Enabled pagedResult.CurrentPage 1; btnNext.Enabled pagedResult.CurrentPage totalPages; }配套的分页按钮事件处理private int _currentPage 1; private const int PageSize 20; private ListEmployee _allEmployees; private void LoadPagedData() { var pagedData DataGridViewHelper.GetPagedData(_allEmployees, PageSize, _currentPage); DataGridViewHelper.BindPagedData(dataGridView1, pagedData, lblPageInfo, btnPrev, btnNext); } private void btnPrev_Click(object sender, EventArgs e) { if (_currentPage 1) { _currentPage--; LoadPagedData(); } } private void btnNext_Click(object sender, EventArgs e) { _currentPage; LoadPagedData(); }3. CRUD操作统一封装实现增删改查的原子操作封装支持实体绑定和批量操作public static T GetSelectedEntityT(DataGridView dgv) where T : class { if (dgv.SelectedRows.Count 0) return null; return dgv.SelectedRows[0].DataBoundItem as T; } public static ListT GetSelectedEntitiesT(DataGridView dgv) where T : class { return dgv.SelectedRows.CastDataGridViewRow() .Select(row row.DataBoundItem as T) .Where(entity entity ! null) .ToList(); } public static void AddEntityT(DataGridView dgv, T entity) { var bindingList dgv.DataSource as BindingListT; if (bindingList ! null) { bindingList.Add(entity); } else if (dgv.DataSource is ListT list) { list.Add(entity); dgv.DataSource null; dgv.DataSource list; } } public static void DeleteSelectedEntitiesT(DataGridView dgv) where T : class { var selected GetSelectedEntitiesT(dgv); if (!selected.Any()) return; var bindingList dgv.DataSource as BindingListT; if (bindingList ! null) { selected.ForEach(e bindingList.Remove(e)); } else if (dgv.DataSource is ListT list) { selected.ForEach(e list.Remove(e)); dgv.DataSource null; dgv.DataSource list; } }使用场景示例// 获取选中实体 var selectedEmployee DataGridViewHelper.GetSelectedEntityEmployee(dataGridView1); // 批量删除 DataGridViewHelper.DeleteSelectedEntitiesEmployee(dataGridView1); // 添加新记录 var newEmployee new Employee { /* 初始化属性 */ }; DataGridViewHelper.AddEntity(dataGridView1, newEmployee);4. 动态列生成与验证根据数据模型自动生成列并支持数据验证public static void GenerateColumnsT(DataGridView dgv, Dictionarystring, string columnConfig null) { dgv.Columns.Clear(); var properties typeof(T).GetProperties(); foreach (var prop in properties) { if (columnConfig ! null !columnConfig.ContainsKey(prop.Name)) continue; var col new DataGridViewColumn(); // 根据属性类型决定列类型 if (prop.PropertyType typeof(bool)) { col new DataGridViewCheckBoxColumn(); } else if (prop.PropertyType typeof(DateTime)) { col new DataGridViewTextBoxColumn(); col.DefaultCellStyle.Format yyyy-MM-dd; } else { col new DataGridViewTextBoxColumn(); } col.DataPropertyName prop.Name; col.HeaderText columnConfig?[prop.Name] ?? prop.Name; col.Name prop.Name; // 添加数据验证 if (prop.PropertyType typeof(int) || prop.PropertyType typeof(decimal)) { col.CellTemplate new DataGridViewTextBoxCell(); col.DefaultCellStyle.Alignment DataGridViewContentAlignment.MiddleRight; // 添加验证事件 dgv.CellValidating (sender, e) { if (dgv.Columns[e.ColumnIndex].Name prop.Name) { if (!int.TryParse(e.FormattedValue.ToString(), out _)) { dgv.Rows[e.RowIndex].ErrorText 必须输入数字; e.Cancel true; } } }; } dgv.Columns.Add(col); } }高级应用 - 条件格式化public static void ApplyConditionalFormatting(DataGridView dgv, string columnName, Funcobject, bool condition, Color? fontColor null, Color? backColor null) { foreach (DataGridViewRow row in dgv.Rows) { if (condition(row.Cells[columnName].Value)) { if (fontColor.HasValue) row.Cells[columnName].Style.ForeColor fontColor.Value; if (backColor.HasValue) row.Cells[columnName].Style.BackColor backColor.Value; } } } // 使用示例将负数列标红 DataGridViewHelper.ApplyConditionalFormatting(dataGridView1, Balance, value Convert.ToDecimal(value) 0, Color.Red);5. Excel导出与打印功能封装将DataGridView数据导出到Excel是常见需求以下封装方法支持自定义导出public static void ExportToExcel(DataGridView dgv, string fileName null) { if (string.IsNullOrEmpty(fileName)) { fileName $Export_{DateTime.Now:yyyyMMddHHmmss}.xlsx; } using (var workbook new XLWorkbook()) { var worksheet workbook.Worksheets.Add(Sheet1); // 添加表头 for (int i 0; i dgv.Columns.Count; i) { if (dgv.Columns[i].Visible) { worksheet.Cell(1, i 1).Value dgv.Columns[i].HeaderText; worksheet.Cell(1, i 1).Style.Font.Bold true; } } // 添加数据行 for (int row 0; row dgv.Rows.Count; row) { if (dgv.Rows[row].IsNewRow) continue; for (int col 0; col dgv.Columns.Count; col) { if (dgv.Columns[col].Visible) { worksheet.Cell(row 2, col 1).Value dgv.Rows[row].Cells[col].Value?.ToString(); } } } // 自动调整列宽 worksheet.Columns().AdjustToContents(); workbook.SaveAs(fileName); } MessageBox.Show($导出成功: {Path.GetFullPath(fileName)}); }打印功能封装public static void PrintDataGridView(DataGridView dgv, string title ) { var printDoc new PrintDocument(); printDoc.DocumentName title; printDoc.PrintPage (sender, e) { Bitmap bitmap new Bitmap(dgv.Width, dgv.Height); dgv.DrawToBitmap(bitmap, new Rectangle(0, 0, dgv.Width, dgv.Height)); e.Graphics.DrawImage(bitmap, e.MarginBounds); }; PrintPreviewDialog preview new PrintPreviewDialog(); preview.Document printDoc; preview.ShowDialog(); }完整DataGridViewHelper类实现将上述功能整合为一个完整的工具类public static class DataGridViewHelper { #region 数据绑定与样式 public static void BindDataT(DataGridView dgv, ListT data, Dictionarystring, string columnMappings null) { // 实现同上... } #endregion #region 分页功能 public static PagedResultT GetPagedDataT(ListT data, int pageSize, int currentPage) { // 实现同上... } public static void BindPagedDataT(DataGridView dgv, PagedResultT pagedResult, Label lblPageInfo, Button btnPrev, Button btnNext) { // 实现同上... } #endregion #region CRUD操作 public static T GetSelectedEntityT(DataGridView dgv) where T : class { // 实现同上... } // 其他CRUD方法... #endregion #region 动态列与验证 public static void GenerateColumnsT(DataGridView dgv, Dictionarystring, string columnConfig null) { // 实现同上... } #endregion #region 导出与打印 public static void ExportToExcel(DataGridView dgv, string fileName null) { // 实现同上... } public static void PrintDataGridView(DataGridView dgv, string title ) { // 实现同上... } #endregion #region 辅助方法 public static void AutoResizeColumns(DataGridView dgv) { dgv.AutoSizeColumnsMode DataGridViewAutoSizeColumnsMode.AllCells; foreach (DataGridViewColumn column in dgv.Columns) { column.AutoSizeMode DataGridViewAutoSizeColumnMode.AllCells; int width column.Width; column.AutoSizeMode DataGridViewAutoSizeColumnMode.None; column.Width width; } } public static void SetReadOnly(DataGridView dgv, bool readOnly true) { dgv.ReadOnly readOnly; dgv.AllowUserToAddRows !readOnly; dgv.AllowUserToDeleteRows !readOnly; } #endregion }实战应用示例下面演示如何在项目中使用这个封装类场景员工管理系统public partial class EmployeeForm : Form { private ListEmployee _employees; private int _currentPage 1; private const int PageSize 15; public EmployeeForm() { InitializeComponent(); InitializeDataGridView(); LoadData(); } private void InitializeDataGridView() { var columnConfig new Dictionarystring, string { { Id, 员工ID }, { Name, 姓名 }, { Department, 部门 }, { HireDate, 入职日期 }, { Salary, 薪资 }, { IsActive, 在职状态 } }; DataGridViewHelper.GenerateColumnsEmployee(dataGridView1, columnConfig); DataGridViewHelper.SetReadOnly(dataGridView1); } private void LoadData() { _employees EmployeeService.GetAllEmployees(); // 从服务层获取数据 LoadPagedData(); } private void LoadPagedData() { var pagedData DataGridViewHelper.GetPagedData(_employees, PageSize, _currentPage); DataGridViewHelper.BindPagedData(dataGridView1, pagedData, lblPageInfo, btnPrev, btnNext); } private void btnAdd_Click(object sender, EventArgs e) { var form new EmployeeEditForm(); if (form.ShowDialog() DialogResult.OK) { DataGridViewHelper.AddEntity(dataGridView1, form.Employee); _employees.Add(form.Employee); // 保持主列表同步 } } private void btnDelete_Click(object sender, EventArgs e) { if (MessageBox.Show(确定删除选中员工吗, 确认, MessageBoxButtons.YesNo) DialogResult.Yes) { DataGridViewHelper.DeleteSelectedEntitiesEmployee(dataGridView1); LoadData(); // 重新加载数据确保同步 } } private void btnExport_Click(object sender, EventArgs e) { DataGridViewHelper.ExportToExcel(dataGridView1, 员工列表.xlsx); } // 分页按钮事件... }性能优化技巧批量操作时暂停绘制public static void SuspendDrawing(DataGridView dgv, Action action) { dgv.SuspendLayout(); try { action(); } finally { dgv.ResumeLayout(); dgv.Refresh(); } } // 使用示例 DataGridViewHelper.SuspendDrawing(dataGridView1, () { // 批量操作代码 });虚拟模式处理大数据量public static void EnableVirtualMode(DataGridView dgv, int totalRowCount, Funcint, object[] rowDataProvider) { dgv.VirtualMode true; dgv.RowCount totalRowCount; dgv.CellValueNeeded (sender, e) { if (e.RowIndex 0 e.RowIndex totalRowCount) { var rowData rowDataProvider(e.RowIndex); if (e.ColumnIndex rowData.Length) { e.Value rowData[e.ColumnIndex]; } } }; }双缓冲提升绘制性能public static void EnableDoubleBuffering(DataGridView dgv) { typeof(DataGridView).InvokeMember(DoubleBuffered, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, dgv, new object[] { true }); }常见问题解决方案问题1数据绑定后列顺序不对解决方案在绑定前明确指定列顺序var columnOrder new Liststring { Id, Name, Department }; foreach (DataGridViewColumn column in dataGridView1.Columns) { column.DisplayIndex columnOrder.IndexOf(column.DataPropertyName); }问题2ComboBox列绑定专用封装方法public static void AddComboBoxColumnT(DataGridView dgv, string propertyName, string headerText, ListT dataSource, string displayMember, string valueMember) { var col new DataGridViewComboBoxColumn { DataPropertyName propertyName, HeaderText headerText, DataSource dataSource, DisplayMember displayMember, ValueMember valueMember, FlatStyle FlatStyle.Flat }; dgv.Columns.Add(col); }问题3行头显示行号private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) { var grid sender as DataGridView; if (grid null) return; using (SolidBrush brush new SolidBrush(grid.RowHeadersDefaultCellStyle.ForeColor)) { e.Graphics.DrawString( (e.RowIndex 1).ToString(), grid.DefaultCellStyle.Font, brush, e.RowBounds.Location.X 20, e.RowBounds.Location.Y 4); } }设计模式应用策略模式实现多种导出格式public interface IExportStrategy { void Export(DataGridView dgv, string fileName); } public class ExcelExportStrategy : IExportStrategy { public void Export(DataGridView dgv, string fileName) { // 实现Excel导出逻辑 } } public class CsvExportStrategy : IExportStrategy { public void Export(DataGridView dgv, string fileName) { // 实现CSV导出逻辑 } } public static class ExportContext { public static void Export(DataGridView dgv, string fileName, IExportStrategy strategy) { strategy.Export(dgv, fileName); } } // 使用示例 ExportContext.Export(dataGridView1, data.csv, new CsvExportStrategy());观察者模式实现数据变更通知public class DataGridViewObserver { private readonly DataGridView _dgv; private readonly ListActionDataGridView _subscribers new ListActionDataGridView(); public DataGridViewObserver(DataGridView dgv) { _dgv dgv; _dgv.DataSourceChanged (s, e) Notify(); } public void Subscribe(ActionDataGridView callback) { _subscribers.Add(callback); } private void Notify() { foreach (var subscriber in _subscribers) { subscriber(_dgv); } } } // 使用示例 var observer new DataGridViewObserver(dataGridView1); observer.Subscribe(dgv { // 数据变化时执行的操作 });单元测试建议为封装方法编写单元测试确保稳定性[TestClass] public class DataGridViewHelperTests { [TestMethod] public void BindData_ShouldSetCorrectDataSource() { // Arrange var dgv new DataGridView(); var testData new ListTestModel { new TestModel { Id 1, Name Test } }; // Act DataGridViewHelper.BindData(dgv, testData); // Assert Assert.AreEqual(1, dgv.Rows.Count); Assert.AreEqual(Test, dgv.Rows[0].Cells[Name].Value); } [TestMethod] public void GetPagedData_ShouldReturnCorrectPage() { // Arrange var data Enumerable.Range(1, 100).Select(i new TestModel { Id i }).ToList(); // Act var result DataGridViewHelper.GetPagedData(data, 10, 2); // Assert Assert.AreEqual(10, result.Data.Count); Assert.AreEqual(11, result.Data.First().Id); } } class TestModel { public int Id { get; set; } public string Name { get; set; } }扩展思路与ORM框架集成public static void BindFromQueryT(DataGridView dgv, IQueryableT query, int pageSize, int pageIndex, Dictionarystring, string columnMappings null) { var pagedQuery query.Skip((pageIndex - 1) * pageSize).Take(pageSize); BindData(dgv, pagedQuery.ToList(), columnMappings); }多语言支持public static void ApplyLanguage(DataGridView dgv, Dictionarystring, string translations) { foreach (DataGridViewColumn column in dgv.Columns) { if (translations.ContainsKey(column.Name)) { column.HeaderText translations[column.Name]; } } }主题系统集成public static void ApplyTheme(DataGridView dgv, Theme theme) { dgv.BackgroundColor theme.BackgroundColor; dgv.GridColor theme.GridColor; dgv.DefaultCellStyle.BackColor theme.CellBackColor; // 其他样式设置... } public class Theme { public Color BackgroundColor { get; set; } public Color GridColor { get; set; } // 其他主题属性... }通过这5个高级封装方法开发者可以大幅提升WinForms项目中DataGridView的开发效率减少重复代码并确保界面行为的一致性。实际项目中可以根据需要进一步扩展这些封装方法添加更多实用功能。
网站建设
高端定制
企业官网