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

 前言:html

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

 

DataGridView能够支持多种数据源格式,好比DataTable和List。优化

DataTable没啥特殊的,自己就是一张二维的表,能够和DataGridView行列对应。this

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

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

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

因而你想固然地会在DataGridView中这样绑定:JobModel.JobNamecode

可是,现实是很骨感的:orm

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

这显然不是咱们想要的结果。对象

因此,咱们要作的就是让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 = 0; i < nameAndProp.Length - 1; i++)
    {
        pObj = GetObject(pObj, nameAndProp[i]);
        if (pObj == null)
        {
            e.Value = string.Empty;
            break;
        }
        if (i == nameAndProp.Length - 2)
        {
            PropertyInfo objectProperty = pObj.GetType().GetProperty(nameAndProp[i + 1]);
            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);
}

 

后话:

正文讲完了,若是你想知道原理的话,能够看我下面的大篇幅的注释说明。

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

 1 //CellFormatting中的代码
 2 if ((dgvLinqDemo.Rows[e.RowIndex].DataBoundItem != null) &&
 3     (dgvLinqDemo.Columns[e.ColumnIndex].DataPropertyName.Contains(".")))
 4 {
 5     //对具备点语法的字段进行分割
 6     //好比JobModel.SkillModel.SkillName
 7     //分割成JobModel,SkillModel和SkillName
 8     string[] nameAndProp = dgvLinqDemo.Columns[e.ColumnIndex].DataPropertyName.Split(new char[] { '.' });
 9 
10     object pObj = dgvLinqDemo.Rows[e.RowIndex].DataBoundItem;
11     //i<nameAndProp.Length-1是由于,只须要循环属性名长度-1次就能够了。
12     //好比,对于JobModel.JobName,在上一步中已经获取了JobModel实体
13     //那么在for循环中的代码只须要执行一遍,即i<nameAndProp.Length-1次,便可获取JobName的属性
14     for (int i = 0; i < nameAndProp.Length - 1; i++)
15     {
16         pObj = GetObject(pObj, nameAndProp[i]);
17         //以JobModel.JobName为例,它只在i=0的时候进来执行一次并获取属性值
18         //那么这里就只能为nameAndProp.Length - 2才能顺利获取到属性值
19         if (i == nameAndProp.Length - 2)
20         {
21             //下面代码中的i+1能够保证它获取的是最后的属性值
22             //即:JobModel.JobName的时候取的是JobName的值
23             //或者JobModel.SkillModel.SkillName的时候取得是SkillName的值
24             PropertyInfo objectProperty = pObj.GetType().GetProperty(nameAndProp[i + 1]);
25             e.Value = objectProperty.GetValue(pObj, null).ToString();//取出字段值
26         }
27     }
28 }
29 
30 /// <summary>
31 /// 经过当前对象和子属性名来获取子对象的实例
32 /// 好比传入UserModel对象和"JobModel"字符串来获取JobModel的实例
33 /// </summary>
34 /// <param name="pObj"></param>
35 /// <param name="nameAndProp"></param>
36 /// <returns></returns>
37 private object GetObject(object pObj, string nameAndProp)
38 {
39     if (pObj == null)
40     {
41         return null;
42     }
43     PropertyInfo objProp = pObj.GetType().GetProperty(nameAndProp);
44     return objProp.GetValue(pObj, null);
45 }

 

优化:

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

可是你发现若是你有好多个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 = 0; i < nameAndProp.Length - 1; i++)
                {
                    pObj = GetObject(pObj, nameAndProp[i]);
                    if (pObj == null)
                    {
                        e.Value = string.Empty;
                        break;
                    }
                    if (i == nameAndProp.Length - 2)
                    {
                        PropertyInfo objectProperty = pObj.GetType().GetProperty(nameAndProp[i + 1]);
                        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#自定义控件添加自定义事件

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

相关文章
相关标签/搜索