基于.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模型类,定于学生包含 的具体属性,如下所示:

cs 复制代码
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类,用于构建数据源,模拟从数据库中获取数据。具体如下所示:

cs 复制代码
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属性中,即可实现重新赋值。

核心代码如下:

cs 复制代码
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布局的最底层部分。如下所示:

cs 复制代码
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事件中设置默认单元格样式,如下所示:

cs 复制代码
this.dgvColumnBirchDate.DefaultCellStyle.Format = "yyyy-MM-dd";

bool类型

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

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

cs 复制代码
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事件中,添加如下代码:

cs 复制代码
dgvData.RowsDefaultCellStyle.BackColor = Color.FromArgb(255, 192, 192);
dgvData.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(192, 255, 255);

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

相关推荐
YCOSA20259 小时前
雨晨 Windows 10 企业版 LTSC x64 中度精简 1809 迎双节 17763.9257 特别版
windows
sukalot9 小时前
windows 驱动实例分析系列: HidHide 驱动分析 - drivers 篇(四)
windows·驱动开发
Neil201311 小时前
ajax跨域请求
c#
唐青枫11 小时前
别把 Razor 当成几段 HTML:C#.NET Razor Pages、MVC 视图与实战详解
c#·.net
ι:14 小时前
NUC12 Pro 安装 Ubuntu 24.04 LTS 详细教程
linux·windows·ubuntu·nuc
YCOSA202515 小时前
微软宣布将于10月7日举行Windows与Surface发布会,聚焦“Windows、NVIDIA RTX Spark、Surface,以及更广泛PC生态”
windows
Source.Liu15 小时前
【A11】 labelprinter(Tauri 版)新建步骤
windows·rust
Punchline27816 小时前
解决 Windows 环境变量 Path 设置时提示“字符太长”的完整指南
windows·环境变量·path
chushiyunen17 小时前
claude code笔记(二)、claude桌面端(内网不好用)
windows
d_benhua17 小时前
以太网测试步骤
windows