基于.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数据表格》的全部内容,旨在抛砖引玉,希望可以一起学习,共同进步。

相关推荐
程序员大辉2 小时前
开源LibreOffice(Office办公套件)下载完整安装教程
开发语言·microsoft·c#
yngsqq2 小时前
运行c#脚本
开发语言·数据库·c#
糯米团子7492 小时前
蓝桥杯javaB组赛前四天复习-1
java·windows·蓝桥杯
bike兔兔2 小时前
Python实现图片批量重命名+统一改名,简单好用图片整理工具
windows
Hugh-Yu-1301233 小时前
WSL --Ubuntu-24.04-LTS子系统安装教程
linux·windows·ubuntu
私人珍藏库14 小时前
[吾爱大神原创工具] QtScreenSwitcher[Win]一个现代化、极简、高颜值的多屏开关工具。
windows·工具·软件·win·多功能
competes14 小时前
慈善基金投资底层逻辑应用 顶层代码低代码配置平台开发结构方式数据存储模块
java·开发语言·数据库·windows·sql
零号全栈寒江独钓16 小时前
基于c/c++实现linux/windows跨平台获取ntp网络时间戳
linux·c语言·c++·windows