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#自定義控件添加自定義事件

如何重寫自定義控件裏的事件

相關文章
相關標籤/搜索