前言:

不想看前言的直接去看正文吧!另外文末有彩蛋。

DataGridView可以支持多种数据源格式,比如DataTable和List。

DataTable没啥特殊的,本身就是一张二维的表,可以和DataGridView行列对应。

但是List不太一样,举个栗子,有一个UserList用户列表,格式是这样的:

class UserModel{
Id
Name,
Gender,
Age,
JobModel
} class JobModel{
JobId,
JobName,
UserId
}

对于上面这种,User中带有一个Job实体的数据结构,Job类中的三个属性在DataGridView中是无法直接显示的。

于是你想当然地会在DataGridView中这样绑定:JobModel.JobName

但是,现实是很骨感的:

工作名称那一栏并没有被匹配到,在最右侧则有一个JobModel的实例则直接被显示了出来。

这显然不是我们想要的结果。

所以,我们要做的就是让DataGridView支持点语法。

那么正文开始!

正文:

好吧,其实正文真的很简单。

只要在DataGridView控件的CellFormatting事件中加入以下代码即可。

其中dgvLinqDemo改成你自己的控件名就好啦:

if ((dgvLinqDemo.Rows[e.RowIndex].DataBoundItem != null) &&
(dgvLinqDemo.Columns[e.ColumnIndex].DataPropertyName.Contains(".")))
{
string[] nameAndProp = dgvLinqDemo.Columns[e.ColumnIndex].DataPropertyName.Split(new char[] { '.' });
object pObj = dgvLinqDemo.Rows[e.RowIndex].DataBoundItem;
for (int i = ; i < nameAndProp.Length - ; i++)
{
pObj = GetObject(pObj, nameAndProp[i]);
if (pObj == null)
{
e.Value = string.Empty;
break;
}
if (i == nameAndProp.Length - )
{
PropertyInfo objectProperty = pObj.GetType().GetProperty(nameAndProp[i + ]);
e.Value = objectProperty.GetValue(pObj, null).ToString();
}
}
} private object GetObject(object pObj, string nameAndProp)
{
if (pObj == null)
{
return null;
}
PropertyInfo objProp = pObj.GetType().GetProperty(nameAndProp);
return objProp.GetValue(pObj, null);
}

后话:

正文讲完了,如果你想知道原理的话,可以看我下面的大篇幅的注释说明。

大致的思路是通过拆分点语法的字符串来通过反射获取下一级的对象或值。

 //CellFormatting中的代码
if ((dgvLinqDemo.Rows[e.RowIndex].DataBoundItem != null) &&
(dgvLinqDemo.Columns[e.ColumnIndex].DataPropertyName.Contains(".")))
{
//对具有点语法的字段进行分割
//比如JobModel.SkillModel.SkillName
//分割成JobModel,SkillModel和SkillName
string[] nameAndProp = dgvLinqDemo.Columns[e.ColumnIndex].DataPropertyName.Split(new char[] { '.' }); object pObj = dgvLinqDemo.Rows[e.RowIndex].DataBoundItem;
//i<nameAndProp.Length-1是因为,只需要循环属性名长度-1次就可以了。
//比如,对于JobModel.JobName,在上一步中已经获取了JobModel实体
//那么在for循环中的代码只需要执行一遍,即i<nameAndProp.Length-1次,即可获取JobName的属性
for (int i = ; i < nameAndProp.Length - ; i++)
{
pObj = GetObject(pObj, nameAndProp[i]);
//以JobModel.JobName为例,它只在i=0的时候进来执行一次并获取属性值
//那么这里就只能为nameAndProp.Length - 2才能顺利获取到属性值
if (i == nameAndProp.Length - )
{
//下面代码中的i+1可以保证它获取的是最后的属性值
//即:JobModel.JobName的时候取的是JobName的值
//或者JobModel.SkillModel.SkillName的时候取得是SkillName的值
PropertyInfo objectProperty = pObj.GetType().GetProperty(nameAndProp[i + ]);
e.Value = objectProperty.GetValue(pObj, null).ToString();//取出字段值
}
}
} /// <summary>
/// 通过当前对象和子属性名来获取子对象的实例
/// 比如传入UserModel对象和"JobModel"字符串来获取JobModel的实例
/// </summary>
/// <param name="pObj"></param>
/// <param name="nameAndProp"></param>
/// <returns></returns>
private object GetObject(object pObj, string nameAndProp)
{
if (pObj == null)
{
return null;
}
PropertyInfo objProp = pObj.GetType().GetProperty(nameAndProp);
return objProp.GetValue(pObj, null);
}

优化:

讲解讲完了,原理也懂了。

但是你发现如果你有好多个DataGridView,就需要写好多CellFormatting代码。

有一百个DataGridView就要写一百次!这显然太蠢了!

所以我们把这些支持点语法的代码封装成一个新的控件。

这样我们只要直接把自定义的控件拖进Winform界面就可以不写任何一行代码就能直接使用点语法啦!

1.创建类库,就像这样:

2.为DataGridViewPro项目添加引用,就像这样:

3.将自动创建的class1.cs改名为DataGridViewPro.cs,然后代码写成样:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms; namespace DataGridViewPro
{
public class DataGridViewPro : DataGridView
{
public DataGridViewPro()
{
InitializeComponent();
} private void InitializeComponent()
{
this.CellFormatting += CellFormattingPro;
} private void CellFormattingPro(object sender, DataGridViewCellFormattingEventArgs e)
{
if ((this.Rows[e.RowIndex].DataBoundItem != null) &&
(this.Columns[e.ColumnIndex].DataPropertyName.Contains(".")))
{
string[] nameAndProp = this.Columns[e.ColumnIndex].DataPropertyName.Split(new char[] { '.' });
object pObj = this.Rows[e.RowIndex].DataBoundItem;
for (int i = ; i < nameAndProp.Length - ; i++)
{
pObj = GetObject(pObj, nameAndProp[i]);
if (pObj == null)
{
e.Value = string.Empty;
break;
}
if (i == nameAndProp.Length - )
{
PropertyInfo objectProperty = pObj.GetType().GetProperty(nameAndProp[i + ]);
e.Value = objectProperty.GetValue(pObj, null).ToString();
}
}
}
} private object GetObject(object pObj, string nameAndProp)
{
if (pObj == null)
{
return null;
}
PropertyInfo objProp = pObj.GetType().GetProperty(nameAndProp);
return objProp.GetValue(pObj, null);
}
}
}

ok,大功告成,把它编译成dll放入你的项目里直接使用带有点语法特性的DataGridViewPro吧!

最后,放上我亲手制作的彩蛋DataGridViewPro.dll,下载引入项目即刻使用!

参考链接:

WinForm创建自定义控件

为C#自定义控件添加自定义事件

如何重写自定义控件里的事件

Winform下让你的DataGridView控件支持点语法(即显示list中的子对象属性)的更多相关文章

  1. C#实现WinForm DataGridView控件支持叠加数据绑定

    我们都知道WinForm DataGridView控件支持数据绑定,使用方法很简单,只需将DataSource属性指定到相应的数据源即可,但需注意数据源必须支持IListSource类型,这里说的是支 ...

  2. winform窗体(六)——DataGridView控件及通过此控件中实现增删改查

    DataGridView:显示数据表,通过此控件中可以实现连接数据库,实现数据的增删改查 一.后台数据绑定:    List<xxx> list = new List<xxx> ...

  3. c# WinForm开发 DataGridView控件的各种操作总结(单元格操作,属性设置)

    一.单元格内容的操作 *****// 取得当前单元格内容 Console.WriteLine(DataGridView1.CurrentCell.Value); // 取得当前单元格的列 Index ...

  4. 转:c# WinForm开发 DataGridView控件的各种操作总结(单元格操作,属性设置)

    一.单元格内容的操作 *****// 取得当前单元格内容 Console.WriteLine(DataGridView1.CurrentCell.Value); // 取得当前单元格的列 Index  ...

  5. Android自己定义控件而且使其能够在xml中自己定义属性

    为什么要自己定义View android开发中自己定义View的优点是显而易见的.比方说以下的这个顶部导航,它被设计出如今应用的每一个界面,但每次的内容却不尽同样.我们不能在每一个layout资源中都 ...

  6. DataGridView控件

    DataGridView控件 DataGridView是用于Windows Froms 2.0的新网格控件.它可以取代先前版本中DataGrid控件,它易于使用并高度可定制,支持很多我们的用户需要的特 ...

  7. DataGridView控件-[引用]

    DataGridView控件 DataGridView是用于Windows Froms 2.0的新网格控件.它可以取代先前版本中DataGrid控件,它易于使用并高度可定制,支持很多我们的用户需要的特 ...

  8. DataGridView控件使用大全说明-各种常用操作与高级操作

    DataGridView控件 DataGridView是用于Windows Froms 2.0的新网格控件.它可以取代先前版本中DataGrid控件,它易于使用并高度可定制,支持很多我们的用户需要的特 ...

  9. DataGridView控件使用大全

    转自:http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html DataGridView控件 DataGridView是用 ...

随机推荐

  1. hdu 4445 Crazy Tank

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  2. 使用route add添加路由,使两个网卡同时访问内外网

    route add命令格式:route [-f] [-p] [Command] [Destination] [mask Netmask] [Gateway] [metric Metric] [if I ...

  3. smtp发送带附件的邮件(直接将string类型结果保存为附件)

    该方式直接保存为HTML文件,也可以是文本文件,其它格式效果不是很好    MailMessage mmsg = new MailMessage();    mmsg.Subject = " ...

  4. EasyUI-datagrid-自动合并单元格

    1.目标 1.1表格初始化完成后,已经自动合并好需要合并的行: 1.2当点击字段排序后,重新进行合并: 2.实现 2.1 引入插件 /** * author ____′↘夏悸 * create dat ...

  5. python3 使用openpyxl库读写excel(续)

    官网:https://openpyxl.readthedocs.io/en/stable/

  6. phpcms pc标签 start不生效的原因

    start 和 page 参数不可同时使用,使用了page参数 会造成start 参数无效

  7. JavaScript高级程序设计(第3版)学习笔记·第8章——浏览器对象模型BOM

    转自:http://www.shaoqun.com/a/43768.aspx 访问和操作浏览器窗口的模型称为浏览器对象模型BOM(Browser Object Model),但习惯上是把所有针对浏览器 ...

  8. html5开放资料

    http://www.cnblogs.com/tim-li/archive/2012/08/06/2580252.html KineticJS教程(12) 摘要: KineticJS教程(12) 作者 ...

  9. 关于ARM立即数的理解

    转自:http://blog.sina.com.cn/s/blog_9bad4f090101e14d.html 一.立即数指令码的组成 ARM公司将32bit指令码分为如下两部分: 32bit立即数通 ...

  10. Effective C++ 38-42

    38.绝不要又一次定义继承而来的缺省參数值. 又一次定义函数缺省參数值意味着又一次定义函数.而非虚函数不能又一次定义,所以将就考虑不能又一次定义虚函数的缺省參数值的原因:虚函数是动态绑定的而缺省參数值 ...