前言:

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

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. 我所遭遇过的游戏中间件--FlashOcx

    使用Flash做游戏界面的另一种方式是通过Abode提供flash.ocx处理Flash界面.将Flash图像通过GDI绘制出来后,再将图像数据拷贝到一个D3D的纹理结构中,最后由引擎的D3D接口进行 ...

  2. C++类模板的声明和定义为什么要放在同一个文件

    不是只能放在.h里面,但是推荐放在.h里面.STL模板实现全部是放在.h里面的.------------------编译能通过.1)参与编译的只是.cpp文件,不会报错的原因,是因为它能在.h里面找到 ...

  3. linux编程合并多个静态库.a为一个.a

    .a 文件的结构和.tar文件就没有什么区别. x 命令解出来, a 命令添加, t命令列表 假设A.a, B.a C.a 在/usr/local/lib目录下 mkdir /tmp/libABC c ...

  4. OTL翻译(8) -- otl_long_string/otl_long_unicode_string类

    otl_long_string/olt_long_unicode_string 这两个类主要用来处理大对象数据.从OTL4.0版本开始,otl_long_string还可以处理任何类型的RAW/BIA ...

  5. 第十四章 springboot + profile(不同环境读取不同配置)

    具体做法: 不同环境的配置设置一个配置文件,例如:dev环境下的配置配置在application-dev.properties中:prod环境下的配置配置在application-prod.prope ...

  6. C/C++ 语言获取文件大小

    在C语言中测试文件的大小,主要使用二个标准函数. 1.fseek 函数原型:int fseek ( FILE * stream, long int offset, int origin ); 参数说明 ...

  7. python环境搭建-Pycharm模块安装方法

    不懂直接看图顺序操作: 方法一: 方法二:

  8. Android之TelephonyManager

    在Android平台中,通过TelephonyManager可以访问与手机通讯相关的信息,比如设备信息.网络信息及SIM卡信息,同时还可以监听电话的相关状态.下面我们通过几个方面来说明Android平 ...

  9. 如何利用Framework模型生成IQD文件

    很多Cognos的新手在接触Transform建模的时候对于iqd文件都有一种朦胧的感觉,当然也不必去死记硬别它的格式,下面我们就来说一下如何用Framework工具来生成iqd文件. 1:打开fra ...

  10. isPostback 的原理及作用(很easy)

    1.IsPostBack用来推断表单是否是回发. (不是第一次请求),是点击表单的提交button回发过来的.是否是回发与get请求还是Post请求无关.可是普通情况下回发都是Post请求. 一般Ge ...