在GridControl表格控件中实现多层级主从表数据的展示

在一些应用场景中,我们需要实现多层级的数据表格显示,如常规的二级主从表数据展示,甚至也有多个层级展示的需求,那么我们如何通过DevExpress的GridControl控表格件实现这种业务需求呢?本篇随笔基于这个需求,对二级、三级的主从表数据进行展示,从而揭开对多层级数据展示的神秘面纱。

1、二级主从表数据展示

主从表数据,我们知道,一个主表记录里面关联有多条明细从表记录,在数据定义的层次上我们体现如下所示。

先定义一个实体类信息作为载体。

    /// <summary>
    /// 记录基础信息
    /// </summary>
    public class DetailInfo
    {
        public DetailInfo()
        {
            this.ID = Guid.NewGuid().ToString();
        }

        /// <summary>
        /// ID标识
        /// </summary>
        public string ID { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 描述信息
        /// </summary>
        public string Description { get; set; }

    }

然后主从表的数据实体类就是除了包含这些信息外,再包含一个子列表(列表信息不一定是同一个实体类),如下所示。

    /// <summary>
    /// 二级层次的列表
    /// </summary>
    public class Detail2Result : DetailInfo
    {
        public List<DetailInfo> Detail2List { get; set; }
    }

这个是我们使用继承关系简化了信息的定义,就是这个实体类包含主表信息外,还包含一个列表集合,属于从表数据的。

有了这些数据的定义,我们构建一些测试的数据,如下所示。

            //创建测试数据
            var result = new Detail2Result()
            {
                Name = "测试",
                Description = "描述内容",
                Detail2List = new List<DetailInfo>()
                {
                    new DetailInfo()
                    {
                        Name = "111测试",
                        Description = "111描述内容"
                    },
                    new DetailInfo()
                    {
                        Name = "222测试",
                        Description = "222描述内容"
                    },
                    new DetailInfo()
                    {
                        Name = "333测试",
                        Description = "333描述内容"
                    }
                }
            };

            //构造一个记录的集合
            var list = new List<Detail2Result>() { result };

这样我们就构建了一个主表从表记录的数据源,可以用于表格控件的绑定的了。

首先我们在界面上创建一个空白的窗体用于演示,并在窗体上增加一个GridControl控件用于展示主从表的数据,如下界面所示。

image

然后,我们可以通过代码创建我们需要的视图信息,如创建主表的GridView显示如下所示。

        /// <summary>
        /// 创建第一个视图
        /// </summary>
        private void CreateGridView()
        {
            var grv = this.gridView1;

            //创建从表显示的列
            grv.Columns.Clear();
            grv.CreateColumn("ID", "ID");//.Visible = false;
            grv.CreateColumn("Name", "名称");
            grv.CreateColumn("Description", "描述内容");

            grv.OptionsBehavior.ReadOnly = false;
            grv.OptionsBehavior.Editable = true;
        }

很简单,我们创建几个列,并指定它的Caption中文显示属性就可以了,然后我们接着还需要创建从表的GridView显示数据,这个是这篇随笔的关键。

具体的代码一次性贴出来,如下所示。

        GridView grv2 = null;
        /// <summary>
        /// 创建第二个视图
        /// </summary>
        private void CreateLevelView()
        {
            var grv = this.gridView1;
            var gridControl = this.gridControl1;

            //创建一个从表的GridView对象
            grv2 = new GridView();
            grv2.ViewCaption = "记录明细";
            grv2.Name = "grv2";
            grv2.GridControl = gridControl;

            //构建GridLevelNode并添加到LevelTree集合里面
            var node = new GridLevelNode();
            node.LevelTemplate = grv2;
            node.RelationName = "Detail2List";//这里对应集合的属性名称
            gridControl.LevelTree.Nodes.AddRange(new GridLevelNode[]
            {
                node
            });

            //添加对应的视图集合显示
            gridControl.ViewCollection.Clear();
            gridControl.ViewCollection.AddRange(new BaseView[] { grv, grv2 });

            //创建从表显示的列
            grv2.Columns.Clear();
            grv2.CreateColumn("ID", "ID");
            grv2.CreateColumn("Name", "名称");
            grv2.CreateColumn("Description", "描述内容");

            //设置非只读、可编辑
            grv2.OptionsBehavior.ReadOnly = false;
            grv2.OptionsBehavior.Editable = true;
        }

我们这里注意到 GridLevelNode 对象,它是我们主从表节点的关键信息,我们需要了解下面部分的代码

            //构建GridLevelNode并添加到LevelTree集合里面
            var node = new GridLevelNode();
            node.LevelTemplate = grv2;
            node.RelationName = "Detail2List";//这里对应集合的属性名称
            gridControl.LevelTree.Nodes.AddRange(new GridLevelNode[]
            {
                node
            });

首先是创建一个节点,然后指定它的 LevelTemplate 为我们新建的GridView,并且他的子集合对象名称为 Detail2List ,最后把这个节点的信息加入到 gridControl.LevelTree.Nodes 里面就可以了,其他的代码就和第一步差不多,指定显示的列和中文显示名称即可。

还有就是我们需要把创建的GridView 加入到指定的集合里面。

            //添加对应的视图集合显示
            gridControl.ViewCollection.Clear();
            gridControl.ViewCollection.AddRange(new BaseView[] { grv, grv2 });

到这里基本上就是大功告成了,剩下的就是数据的绑定处理了。前面我们已经介绍了实体类的准备工作和创建测试数据的代码,那么我们这里沿用上面的代码进行数据的绑定就可以了。如下代码所示。

        /// <summary>
        /// 绑定数据源
        /// </summary>
        private void BindData()
        {
            //创建测试数据
            var result = new Detail2Result()
            {
                Name = "测试",
                Description = "描述内容",
                Detail2List = new List<DetailInfo>()
                {
                    new DetailInfo()
                    {
                        Name = "111测试",
                        Description = "111描述内容"
                    },
                    new DetailInfo()
                    {
                        Name = "222测试",
                        Description = "222描述内容"
                    },
                    new DetailInfo()
                    {
                        Name = "333测试",
                        Description = "333描述内容"
                    }
                }
            };

            //构造一个记录的集合
            var list = new List<Detail2Result>() { result };

            //绑定数据源
            this.gridControl1.DataSource = list;
        }

        private void FrmTestDetails_Load(object sender, EventArgs e)
        {
            BindData();
        }

我们来运行下完成的程序界面,可以看到例子的效果界面如下所示。

image

我们可以看到数据记录是有树形节点的,展开就可以看到明细记录了,这个就是我们这里介绍的二级主从表数据的展示效果。

2、三级主从表数据展示

上面介绍了二级主从表的数据展示,其实GridControl可以用于展示三级以及更多层级的数据展示,只要你的数据设计合理,就可实现多层级的正确展示的。

本小节介绍三级的主从表数据展示,和二级数据展示类似,不过我们进一步实现了多层级的处理而已。

我们在二级层次的数据上定义了一个三级层次的数据实体类,如下所示。

    /// <summary>
    /// 二级层次的列表
    /// </summary>
    public class Detail2Result : DetailInfo
    {
        public List<DetailInfo> Detail2List { get; set; }
    }

    /// <summary>
    /// 三级层次的列表
    /// </summary>
    public class Detail3Result : DetailInfo
    {
        public List<Detail2Result> Detail3List { get; set; }
    }

三级层次的测试数据初始化如下所示:

            //创建测试数据
            var result = new Detail3Result()
            {
                Name = "测试11",
                Description = "描述内容11",
                //二级列表
                Detail3List = new List<Detail2Result>()
                {
                    new Detail2Result()
                    {
                        Name = "测试22",
                        Description = "描述内容22",
                             
                        //三级列表
                        Detail2List = new List<DetailInfo>() 
                        {
                            new DetailInfo()
                            {
                                Name = "31测试",
                                Description = "31描述内容"
                            },
                            new DetailInfo()
                            {
                                Name = "32测试",
                                Description = "32描述内容"
                            },
                            new DetailInfo()
                            {
                                Name = "33测试",
                                Description = "33描述内容"
                            }
                        }
                    }
                }
            };

            //构造一个记录的集合
            var list = new List<Detail3Result>() { result };

和二级层次的处理步骤类似,我们先创建主表的信息展示,如下所示。

        /// <summary>
        /// 创建第一个视图
        /// </summary>
        private void CreateGridView()
        {
            var grv = this.gridView1;
            var gridControl = this.gridControl1;

            //创建从表显示的列
            grv.Columns.Clear();
            grv.CreateColumn("ID", "ID");//.Visible = false;
            grv.CreateColumn("Name", "名称");
            grv.CreateColumn("Description", "描述内容");

            grv.OptionsBehavior.ReadOnly = false;
            grv.OptionsBehavior.Editable = true;
        }

然后着手创建二级、三级的列表信息展示,

        GridView grv2 = null;
        GridView grv3 = null;
        /// <summary>
        /// 创建第二个视图
        /// </summary>
        private void CreateLevelView()
        {
            var grv = this.gridView1;
            var gridControl = this.gridControl1;

            //创建一个二级从表的GridView对象
            grv2 = new GridView();
            grv2.ViewCaption = "记录明细";
            grv2.Name = "grv2";
            grv2.GridControl = gridControl;

            //创建一个三级从表的GridView对象
            grv3 = new GridView();
            grv3.ViewCaption = "记录明细2";
            grv3.Name = "grv3";
            grv3.GridControl = gridControl;

这样我们相当于创建多两个(总共三个GridView对象)用于展示数据列表。

接着最为关键的是主从关系的节点,我们可以简单的理解他的Node节点和我们树形列表的Node处理方式类似即可。

            //构建GridLevelNode
            var topNode = new GridLevelNode();
            topNode.LevelTemplate = grv2;           //这里是对应的视图
            topNode.RelationName = "Detail3List";   //这里对应集合的属性名称

             //构建GridLevelNode
            var secondNode = new GridLevelNode();
            secondNode.LevelTemplate = grv3;        //这里是对应的视图
            secondNode.RelationName = "Detail2List";//这里对应集合的属性名称

            //需要添加节点的层级关系,类似Tree节点处理
            topNode.Nodes.Add(secondNode);
            //最后添加节点到集合里面
            gridControl.LevelTree.Nodes.Add(topNode);

通过定义两个GridLevelNode,然后指定他们的Node关系( topNode.Nodes.Add(secondNode) ),这样我们就可以很清晰的关联起来它们的节点关系了。

最后是把我们创建的几个视图加入到集合里面,并设定一些关系即可。

            //添加对应的视图集合显示
            gridControl.ViewCollection.Clear();
            gridControl.ViewCollection.AddRange(new BaseView[] { grv, grv2, grv3 });

            //创建从表显示的列
            grv2.Columns.Clear();
            grv2.CreateColumn("ID", "ID");
            grv2.CreateColumn("Name", "名称");
            grv2.CreateColumn("Description", "描述内容");
                                 
            //创建从表显示的列
            grv3.Columns.Clear();
            grv3.CreateColumn("ID", "ID");
            grv3.CreateColumn("Name", "名称");
            grv3.CreateColumn("Description", "描述内容");

            //设置非只读、可编辑
            grv2.OptionsBehavior.ReadOnly = false;
            grv2.OptionsBehavior.Editable = true;

            //设置非只读、可编辑
            grv3.OptionsBehavior.ReadOnly = false;
            grv3.OptionsBehavior.Editable = true;

整个部分的代码如下所示。

        GridView grv2 = null;
        GridView grv3 = null;
        /// <summary>
        /// 创建第二个视图
        /// </summary>
        private void CreateLevelView()
        {
            var grv = this.gridView1;
            var gridControl = this.gridControl1;

            //创建一个二级从表的GridView对象
            grv2 = new GridView();
            grv2.ViewCaption = "记录明细";
            grv2.Name = "grv2";
            grv2.GridControl = gridControl;

            //创建一个三级从表的GridView对象
            grv3 = new GridView();
            grv3.ViewCaption = "记录明细2";
            grv3.Name = "grv3";
            grv3.GridControl = gridControl;

            //构建GridLevelNode
            var topNode = new GridLevelNode();
            topNode.LevelTemplate = grv2;           //这里是对应的视图
            topNode.RelationName = "Detail3List";   //这里对应集合的属性名称

             //构建GridLevelNode
            var secondNode = new GridLevelNode();
            secondNode.LevelTemplate = grv3;        //这里是对应的视图
            secondNode.RelationName = "Detail2List";//这里对应集合的属性名称

            //需要添加节点的层级关系,类似Tree节点处理
            topNode.Nodes.Add(secondNode);
            //最后添加节点到集合里面
            gridControl.LevelTree.Nodes.Add(topNode);

            //添加对应的视图集合显示
            gridControl.ViewCollection.Clear();
            gridControl.ViewCollection.AddRange(new BaseView[] { grv, grv2, grv3 });

            //创建从表显示的列
            grv2.Columns.Clear();
            grv2.CreateColumn("ID", "ID");
            grv2.CreateColumn("Name", "名称");
            grv2.CreateColumn("Description", "描述内容");
                                 
            //创建从表显示的列
            grv3.Columns.Clear();
            grv3.CreateColumn("ID", "ID");
            grv3.CreateColumn("Name", "名称");
            grv3.CreateColumn("Description", "描述内容");

            //设置非只读、可编辑
            grv2.OptionsBehavior.ReadOnly = false;
            grv2.OptionsBehavior.Editable = true;

            //设置非只读、可编辑
            grv3.OptionsBehavior.ReadOnly = false;
            grv3.OptionsBehavior.Editable = true;
        }

也就是我们在窗体初始化的时候,创建它们的视图关系即可,如下代码所示。

    /// <summary>
    /// 测试三级主从明细列表
    /// </summary>
    public partial class FrmTestDetails2 : BaseForm
    {
        public FrmTestDetails2()
        {
            InitializeComponent();

            CreateGridView();
            CreateLevelView();
        }

最后就是数据源的绑定操作了,这个利用前面介绍过的准备数据即可。

        private void FrmTestDetails2_Load(object sender, EventArgs e)
        {
            BindData();
        }

        /// <summary>
        /// 绑定数据源
        /// </summary>
        private void BindData()
        {
            //创建测试数据
            var result = new Detail3Result()
            {
                Name = "测试11",
                Description = "描述内容11",
                //二级列表
                Detail3List = new List<Detail2Result>()
                {
                    new Detail2Result()
                    {
                        Name = "测试22",
                        Description = "描述内容22",
                             
                        //三级列表
                        Detail2List = new List<DetailInfo>() 
                        {
                            new DetailInfo()
                            {
                                Name = "31测试",
                                Description = "31描述内容"
                            },
                            new DetailInfo()
                            {
                                Name = "32测试",
                                Description = "32描述内容"
                            },
                            new DetailInfo()
                            {
                                Name = "33测试",
                                Description = "33描述内容"
                            }
                        }
                    }
                }
            };

            //构造一个记录的集合
            var list = new List<Detail3Result>() { result };

            //绑定数据源
            this.gridControl1.DataSource = list;
        }

以上就是三级层次的关系处理,如果我们理解了,其他更多层级的数据展示也是依照这个规则,增加节点和视图即可,原理一样。

案例的效果如下所示。

image

3、利用分页控件实现数据的展示

上面的两个案例是基于DevExpress的内置表格控件GridControl进行处理的,我们在Winform框架的开发过程中,往往为了效率和分页方便,一般都是使用分页控件来展示数据的,那么利用分页控件实现多层级的数据展示是如何的呢?

其实基本步骤也是差不多的,只是主表视图使用分页控件即可,如下所示。

image
    /// <summary>
    /// 数据指定的主从表展示
    /// </summary>    
    public partial class FrmDictTypeMasterDetail : BaseDock
    {
        public FrmDictTypeMasterDetail()
        {
            InitializeComponent();

            InitDictItem();

            this.winGridViewPager1.OnPageChanged += new EventHandler(winGridViewPager1_OnPageChanged);
            this.winGridViewPager1.OnStartExport += new EventHandler(winGridViewPager1_OnStartExport);
            this.winGridViewPager1.OnDeleteSelected += new EventHandler(winGridViewPager1_OnDeleteSelected);
            this.winGridViewPager1.OnRefresh += new EventHandler(winGridViewPager1_OnRefresh);
            this.winGridViewPager1.AppendedMenu = this.contextMenuStrip1;
            this.winGridViewPager1.ShowLineNumber = true;
            this.winGridViewPager1.BestFitColumnWith = false;//是否设置为自动调整宽度,false为不设置
            this.winGridViewPager1.gridView1.DataSourceChanged += new EventHandler(gridView1_DataSourceChanged);
            this.winGridViewPager1.gridView1.CustomColumnDisplayText += new DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventHandler(gridView1_CustomColumnDisplayText);
            this.winGridViewPager1.gridView1.RowCellStyle += new DevExpress.XtraGrid.Views.Grid.RowCellStyleEventHandler(gridView1_RowCellStyle);

            CreateLevelView();
            RegisterEvent();
        }

        GridView grv2 = null;
        private void CreateLevelView()
        {
            var grv = this.winGridViewPager1.GridView1;
            var gridControl = this.winGridViewPager1.gridControl1;

            //创建一个从表的GridView对象
            grv2 = new GridView();
            grv2.ViewCaption = "记录明细";
            grv2.Name = "grv2";
            grv2.GridControl = gridControl;

            //构建GridLevelNode并添加到LevelTree集合里面
            var node = new GridLevelNode();
            node.LevelTemplate = grv2;
            node.RelationName = "Children";
            gridControl.LevelTree.Nodes.AddRange(new GridLevelNode[]
            {
                node
            });

            gridControl.ViewCollection.Clear();
            gridControl.ViewCollection.AddRange(new BaseView[] { grv, grv2 });

            //创建从表显示的列
            grv2.Columns.Clear();
            grv2.CreateColumn("ID", "ID").Visible =false; //标识行的关键字,可用于删除处理
            grv2.CreateColumn("DictType_ID", "DictType_ID").Visible = false;//创建一个字段,隐藏的,存储记录
            grv2.CreateColumn("Name", "项目名称");
            grv2.CreateColumn("Value", "项目值");
            grv2.CreateColumn("Seq", "排序");
            grv2.CreateColumn("Remark", "备注");

            grv2.OptionsBehavior.ReadOnly = false;
            grv2.OptionsBehavior.Editable = true;
            grv2.DataSourceChanged += grv2_DataSourceChanged;
        }

以上就是基于GridControl实现数据的主从关系的处理,可以实现多层级的展示,希望这些案例能够对你展示数据有所帮助。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容