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

基于.NET的Windows窗体编程之WinForms数据表格

在实际应用开发中,我们常常需要向用户展示数据,而数据的呈现方式通常以二维表格,折线图,柱状图等图表为主,最常见的就是表格,如销售的统计报表,明细列表等。今天我们以一些简单的小例子,简述在基于.NET的Windows窗体编程中数据表格的应用,仅供学习分享使用,如有不足之处,还请指正。

概述

在WinForms开发中,数据表格控件为DataGridView,它提供了一种强大且灵活的方法来显示表格格式数据。使用DataGridView控件,可以显示和编辑来自多种不同类型的数据源的表格数据,同时DataGridView 控件高度可配置且可扩展,它提供许多属性、方法和事件来自定义其外观和行为。建议在 Windows 窗体应用程序中显示表格数据时,优先使用 DataGridView 控件,而不是使用其他控件(例如,DataGrid)。

将数据绑定到 DataGridView 控件非常简单,只需要设置 DataSource 属性即可,如果绑定的数据源包含多个列表或表,则需要将 DataMember 属性设置为指定要绑定到的列表或表的名称。DataGridView 控件支持标准 Windows 窗体数据绑定模型,它可以绑定到以下数据类型列表:

  • 实现 IList 接口的任何类,包括一维数组。
  • 实现 IListSource 接口的任何类,例如 DataTable 和 DataSet 类。
  • 实现 IBindingList 接口的任何类,如 BindingList<T> 类。
  • 实现 IBindingListView 接口的任何类,如 BindingSource 类。

通常,将DataGridView绑定到 BindingSource 组件,并将 BindingSource 组件绑定到另一个数据源,或者使用业务对象填充BindingSource 。BindingSource 组件是首选数据源,它可以绑定到各种数据源,并且可以自动解决许多数据绑定问题。那什么是BindingSource呢?

BindingSource

BindingSource,它封装一个数据源以便绑定到控件,此组件有两个用途:

  1. 将一个窗体的DataGridView控件绑定到数据时,该组件会提供一个间接层。 要实现此目标,可以先将 BindingSource 组件绑定到你的数据源,然后再将窗体上的控件绑定到 BindingSource 组件。 与数据的所有进一步交互(包括导航、排序、筛选和更新)都是通过调用 BindingSource 组件来完成的。
  2. BindingSource 组件可以充当强类型的数据源。 使用 BindingSource 方法将类型添加到 Add 组件将创建该类型的列表。

下图展示了 BindingSource 组件在现有数据绑定架构中的角色和位置。

同时对于需要导航窗体上数据的用户,BindingNavigator 组件使你能够与 BindingSource 组件协调导航和作数据。

关键属性

DataGridView关键属性,主要有以下几个:

  • Name,DataGridView控件在Form表单中的唯一标识。
  • Dock,定义空间的停靠方式,主要有None,Top,Bottom,Left,Right,Fill等几种方式,默认为None(不停靠),通过Location定义控件在父容器中的位置。
  • Columns,DataGridView控件的列属性,点击它可以打开“编辑列”对话框。
  • DataMember,如果绑定的数据源有多个子表时,可以通过DataMember属性指定需要绑定的数据表。
  • MultiSelect,标识是否可以一次选择多个单元格或行或列。
  • DataSource,表示DataGridView控件绑定的数据源。
  • AutoGenerateColumns 是否根据数据源中的数据自动生成列,默认为true。

添加或编辑列

在Visual Studio设计器中,选中DataGridView控件,然后点击右侧的箭头,在弹出的“DataGridView任务”菜单中,可以点击“添加列...”或“编辑列...”,建议点击“编辑列...”,因为在弹出的“编辑列”窗口中,即可以添加列,也可以编辑现有列。如下图所示:

在“编辑列”窗口中,可以设置添加列,或编辑现有列,DataGridView控件的数据列,主要有以下常用属性:

  • Name,指定数据列的唯一标识,它在Form表单或UserControl中具有唯一性。
  • ColumnType,指定数据列的类型,表示数据将以何种形式呈现,它具有几种选择:
    • DataGridViewTextBoxColumn,文本的形式展示数据
    • DataGridViewImageColumn,列数据可以展示图片
    • DataGridViewCheckBoxColumn,复选框的形式展示数据
    • DataGridViewComoboBoxColumn,下拉框的形式展示数据
    • DataGridViewButtonColumn,按钮的形式展示数据
    • DataGridViewLinkColumn,超链接的形式展示数据。
  • DataPropertyName,指定数据列绑定到数据源的列名称,用于标识显示数据源的哪一列。
  • HeaderText,列标题,表示列标题单元格的文本内容。
  • Visible,指定列是否可见,true:显示数据列,false:隐藏数据列。
  • ReadOnly,指定用户是否可以编辑列,true:不可编辑,false:可编辑。
  • Frozen,是否冻结列,水平滚动条移动时,列是否移动。
  • Width,列的宽度,MinimumWidth,列的最小宽度。

注意:DataGridView默认会勾选“启用添加”,“启用编辑”,“启用删除”等功能,如果不需要,可以在点击“右箭头”弹出的“DataGridView任务”菜单中取消其功能。

DataGridView实例

接下来以一个实际案例,简述DataGridView控件的相关用法。

1. 构建数据源

通过BindingSource组件,数据源的支持所有IEnumerable 接口的实现,今天以学生列表为例,首先创建Student模型类,定于学生包含 的具体属性,如下所示:

namespace Okcoder.WinForm.Demo { /// <summary> /// 学生信息 /// </summary> public class Student { /// <summary> /// 唯一标识 /// </summary> public int Id { get; set; } /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 年龄 /// </summary> public int Age { get; set; } /// <summary> /// 性别 true:男 false:女 /// </summary> public bool Gender { get; set; } /// <summary> /// 是否OK,true:是 false:否 /// </summary> public bool IsOker { get; set; } /// <summary> /// 出生日期 /// </summary> public DateTime BirthDate { get; set; } } }

创建SqlDataHelper类,用于构建数据源,模拟从数据库中获取数据。具体如下所示:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Okcoder.WinForm.Demo { /// <summary> /// 数据帮助类 /// </summary> public class SqlDataHelper { private List<Student> students; /// <summary> /// 构造函数 /// </summary> public SqlDataHelper() { InitDataInfo(); } /// <summary> /// 使用列表,模拟数据库数据,假定数据库中有20名学生信息 /// </summary> private void InitDataInfo() { students = new List<Student>(); students.Add(new Student() { Id = 1, Name = "张三丰", Age = 100, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-100) }); students.Add(new Student() { Id = 2, Name = "宋远桥", Age = 50, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-50) }); students.Add(new Student() { Id = 3, Name = "俞莲舟", Age = 48, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-48) }); students.Add(new Student() { Id = 4, Name = "俞岱岩", Age = 46, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-46) }); students.Add(new Student() { Id = 5, Name = "张松溪", Age = 44, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-44) }); students.Add(new Student() { Id = 6, Name = "张翠山", Age = 45, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-45) }); students.Add(new Student() { Id = 7, Name = "殷梨亭", Age = 43, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-43) }); students.Add(new Student() { Id = 8, Name = "莫声谷", Age = 42, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-42) }); students.Add(new Student() { Id = 9, Name = "宋青书", Age = 20, Gender = true, IsOker = false, BirthDate = DateTime.Now.AddYears(-20) }); students.Add(new Student() { Id = 10, Name = "张无忌", Age = 18, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(18) }); students.Add(new Student() { Id = 11, Name = "郭襄", Age = 100, Gender = true, IsOker = true, BirthDate = DateTime.Now.AddYears(-100) }); students.Add(new Student() { Id = 12, Name = "灭绝师太", Age = 51, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-51) }); students.Add(new Student() { Id = 13, Name = "周芷若", Age = 18, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-18) }); students.Add(new Student() { Id = 14, Name = "定敏君", Age = 19, Gender = false, IsOker = false, BirthDate = DateTime.Now.AddYears(-19) }); students.Add(new Student() { Id = 15, Name = "纪晓芙", Age = 43, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-43) }); students.Add(new Student() { Id = 16, Name = "静玄", Age = 30, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-30) }); students.Add(new Student() { Id = 17, Name = "静虚", Age = 29, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-29) }); students.Add(new Student() { Id = 18, Name = "静空", Age = 28, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-28) }); students.Add(new Student() { Id = 19, Name = "静慧", Age = 27, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-27) }); students.Add(new Student() { Id = 20, Name = "静迦", Age = 26, Gender = false, IsOker = true, BirthDate = DateTime.Now.AddYears(-26) }); } /// <summary> /// 查询学生列表 /// </summary> /// <param name="name"></param> /// <returns></returns> public List<Student> Query(string name) { if (string.IsNullOrEmpty(name)) { return this.students; } var list = this.students.Where(item => item.Name.Contains(name)).ToList(); return list; } /// <summary> /// 根据ID获取学生信息 /// </summary> /// <param name="id"></param> /// <returns></returns> public Student? Get(int id) { return students.FirstOrDefault(item => item.Id == id); } /// <summary> /// 添加学生 /// </summary> /// <param name="name"></param> /// <param name="age"></param> /// <param name="gender"></param> /// <param name="isoker"></param> /// <param name="birthdate"></param> public void Add(string name,int age,bool gender,bool isoker,DateTime birthdate) { if (students.Count(item => item.Name == name) < 1) { int id = students.Count > 0 ? students.Max(item => item.Id) + 1 : 1; students.Add(new Student() { Id = id, Name = name, Age = age, Gender = gender, IsOker = isoker, BirthDate = birthdate }); } } /// <summary> /// 删除学生 /// </summary> /// <param name="id"></param> public void Delete(int id) { var student = students.FirstOrDefault(item => item.Id == id); if (student != null) { this.students.Remove(student); } } /// <summary> /// 编辑学生 /// </summary> /// <param name="id"></param> /// <param name="name"></param> /// <param name="age"></param> /// <param name="gender"></param> /// <param name="isoker"></param> /// <param name="birthdate"></param> public void Edit(int id, string name, int age, bool gender, bool isoker, DateTime birthdate) { var student = students.FirstOrDefault(item => item.Id == id); if (student != null) { student.Name = name; student.Age = age; student.Gender = gender; student.IsOker = isoker; student.BirthDate = birthdate; } } } }

2. UI布局

在本实例中,通过两个SplitContainer布局容器控件实现上,中,下布局结构:

  • 上边为查询条件,按钮等功能,实现数据列表的搜索。
  • 中间为数据区,主要存放DataGridView控件,显示学生列表数据。
  • 底部为导航区,主要放置BingNavigator控件。

设置步骤:

  1. 拖动DataGridView控件到数据区,且设置DataGridView控件的Dock属性为Fill,实现自动填充,并设置Name属性为dgvData。
  2. 拖动BindingSource组件到Form表单,由于BindingSource作为数据中间层,并没有UI呈现,所以在Form设计器的最底层,设置Name属性为bdsData。
  3. 将DataGridView的DataSource属性设置为上面的BindingSource组件,通过指定名称bdsData进行关联。
  4. 设置DataGridView需要显示的列,根据Student属性,为DataGridView添加需要显示的列,并设置对应的DataPropertyName属性。

具体如下所示:

3. 加载数据

在Form表单的类文件中,启动页面加载数据需要以下步骤:

  1. 初始化SqlDataHelper实例对象,
  2. Form表单的Load事件方法中,获取学生信息列表,并赋值给BindingSource组件的DataSource属性。
  3. 在Query按钮的事件处理方法中,根据用户输入的检索条件进行查询,将获取的列表重新Binding到BindingSource的DataSource属性中,即可实现重新赋值。

核心代码如下:

namespace Okcoder.WinForm.Demo { public partial class FrmData : Form { private SqlDataHelper sqlDataHelper; public FrmData() { InitializeComponent(); sqlDataHelper = new SqlDataHelper(); } private void FrmData_Load(object sender, EventArgs e) { List<Student> students = sqlDataHelper.Query(string.Empty); this.bdsData.DataSource = students; } private void btnQuery_Click(object sender, EventArgs e) { string name = this.txtName.Text.Trim(); List<Student> students = sqlDataHelper.Query(name); this.bdsData.DataSource = students; } } }

增加BindingNavigator,由于BindingNavigator并不在工具箱中出现,无法通过设计器拖动到Form表单中,但可以通过后台代码添加。本实例将BindingNavigator控件添加到UI布局的最底层部分。如下所示:

private void FrmData_Load(object sender, EventArgs e) { List<Student> students = sqlDataHelper.Query(string.Empty); this.bdsData.DataSource = students; //添加BindingNavigator BindingNavigator bdNav = new BindingNavigator(true); bdNav.BindingSource = bdsData; // 需先创建 BindingSource bdNav.Dock = DockStyle.Top; this.splitContainer2.Panel2.Controls.Add(bdNav); }

4. 运行示例

运行实例,效果如下所示:

在上述示例中,还存在4个问题:

  1. Gender是性别,希望看到男,女,而不是True,False。
  2. IsOker是否正派人士,希望看到是,否,而不是True,False。
  3. BirthDate是出生日期,希望看到年月日即可,不需要看到时分秒。
  4. DataGridView数据表格中每一行的背景颜色都相同,我们希望交替使用不同的背景色。

接下来将分别解决这4个问题。

日期格式

首先打开“编辑列”对话框,然后选择BirthDate列,在右侧的属性框中,选择DefaultCellStyle,然后点击右侧的“...”按钮,打开“CellStyle生成器”如下所示:

在“CellStyle生成器”中,选择Format,点击右侧的“...”按钮,打开“格式化字符串”对话框,如下所示:

在“格式化字符串”对话框,选择日期,可以选择需要显示的格式,如:“yyyy/MM/dd”。

也可以自定义格式,本示例自定义格式为“yyyy-MM-dd”,如下所示:

设置格式后,BirthDate显示内容如下所示:

也可通过后台代码进行设置,如在Form的Load事件中设置默认单元格样式,如下所示:

this.dgvColumnBirchDate.DefaultCellStyle.Format = "yyyy-MM-dd";

bool类型

为了将bool类型的值显示为更加语义化的文本,可以订阅DataGridView控件的CellFormatting事件。首先选择DataGridView控件,在右侧的属性窗口中,点击(⚡️)切换到事件Tab页,然后找到CellFormatting事件,在右侧的框中双击,即可为DataGridView控件添加相应事件。 如下所示:

在生成的dgvData_CellFormatting事件中,根据bool的值,重新为单元格赋值,如下所示:

private void dgvData_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (e.ColumnIndex == 3) { //Gender if (e.Value != null && ((bool)e.Value)) { e.Value = "男"; } else { e.Value = "女"; } } if (e.ColumnIndex == 4) { //IsOker if (e.Value != null && ((bool)e.Value)) { e.Value = "是"; } else { e.Value = "否"; } } }

格式化后,数据显示如下所示:

行交替样式

表格数据通常以类似账本的格式向用户呈现,其中交替行具有不同的背景色,也是常用功能之一。通过这种格式,用户可以更轻松地识别每行中的单元格,特别是在有很多列的宽表格中。

首先选择DataGridView,然后在属性对话框中,选择RowDefaultCellStyle,在点击右侧的“...”按钮,弹出“CellStyle生成器”,在其中设置BackColor属性,如下所示:

接着以同样方式设置AlternatingRowsDefaultCellStyle属性,中对应的BackColor属性,如下所示:

设置交替背景色后,效果如下所示:

上述交替行设置背景色效果,也可以在Form表单的Load事件中,添加如下代码:

dgvData.RowsDefaultCellStyle.BackColor = Color.FromArgb(255, 192, 192); dgvData.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(192, 255, 255);

以上就是《基于.NET的Windows窗体编程之WinForms数据表格》的全部内容,旨在抛砖引玉,希望可以一起学习,共同进步。

http://www.cnnetsun.cn/news/2003928.html

相关文章:

  • 华为OD机试真题 新系统 - 准备生日礼物 (Java/Py/C/C++/Js/Go)
  • # 发散创新:基于Selenium的自动化测试框架重构与实战优化在当今快速迭代的软件开
  • **用Python + DeepLearning玩转AI艺术:从代码到生成式视觉创
  • AB测试自动化革命-动态因果分流与指标自治
  • 2025届最火的五大降AI率网站实测分析
  • aibiye爱毕业是一款免费查重平台,每日检测次数无限制,AI改写功能助力论文降重更高效
  • MongoDB中什么是Hashed Shard Key的哈希冲突_哈希函数的分布均匀性分析
  • 【紧急通告】C# 14原生AOT已成Dify企业版合同SLA新增条款!未启用AOT部署的客户将于2025 Q3起暂停远程模型热更新支持——立即获取迁移检查表与ROI测算器
  • 多维度拆透渲染引擎 第一篇【维度:定义】概念正本清源 —— 渲染引擎的本质与“引擎性“
  • 基于Vue3与TypeScrt开发的Three.js汽车展厅源码
  • MusicBee网易云歌词插件:打造专业级音乐播放体验
  • 新概念英语第二册16_A polite request
  • Bilibili视频下载神器:5分钟掌握跨平台B站视频下载技巧
  • 告别手写Shader!用Unity ShaderGraph从零实现一个卡通水面(URP 2022.3 LTS)
  • 【限时开源】Dify文档解析性能诊断工具集(含PDF结构分析热力图、文本流失追踪器、Embedding一致性校验器)
  • Spring事务同步器TransactionSynchronizationAdapter:除了afterCommit,这几个回调方法你用对了吗?
  • 深入ego_planner状态机:从代码层面理解XTDrone中无人机的重规划决策逻辑
  • 远程工作骗局:隐形加班——软件测试从业者的专业困境与破局之道
  • 告别XDMA!用AXI Bridge实现FPGA主动读写PC内存(附WinDriver测试与中断配置)
  • 大学生党务学习平台小程序(文档+源码)_kaic
  • 2025年09月CCF-GESP编程能力等级认证Python编程三级真题解析
  • 无线调试中的端口转发问题
  • 拆解网红小风扇:它的‘边充边放’和‘过路保护’是怎么用一颗FS8A15S8 MCU实现的?
  • 量子退火实战避坑指南:约束条件转哈密顿量,你的M值真的设对了吗?
  • 告别轮询!S32K144外部中断配置保姆级教程(基于S32 Design Studio和SDK)
  • 动力电池系统继电器选型与故障诊断实战指南
  • 从电影分类到用户画像:用Hive lateral view + explode搞定业务中的‘一对多’数据清洗
  • 别再踩坑了!uni-app开发App请求报错statusCode:-1的终极排查指南(附真机调试技巧)
  • 从职场回归考场:一位十年工龄工程师的北航MEM备考实战复盘
  • 用ESP32和0.96寸OLED做个桌面小摆件:实时显示天气/时间(附完整源码)