1. 为什么要在DataGridView里做树形结构?
如果你做过WinForms开发,肯定对DataGridView这个控件不陌生。它就像一张Excel表格,规规矩矩地展示数据,一行一列清清楚楚。但有时候,我们拿到的数据不是平铺直叙的,它是有层级的,就像文件管理器里的文件夹和文件,或者公司里的部门架构。这时候,如果还用普通的DataGridView,把所有数据都摊开,用户看着一长串平级的条目,根本理不清头绪,体验非常差。
我最早遇到这个问题,是在做一个内部的项目管理系统。每个项目下面有多个任务,每个任务下面又有子任务和具体的活动。产品经理拿着原型图过来,指着那个带“小三角”可以展开收起的列表问我:“这个效果,用DataGridView能做吗?”我当时第一反应是,DataGridView不是干这个的,应该去找第三方的树形控件。但转念一想,项目里已经大量使用了DataGridView,风格统一,操作习惯用户也熟悉,为了这一个功能引入一个全新的控件库,有点得不偿失。而且,很多第三方树控件功能虽然强大,但定制起来反而更复杂。
所以,我就琢磨着,能不能让DataGridView“变身”,让它既能保持表格的规整,又能拥有树形结构的层级展示和交互能力。说白了,就是在DataGridView的“躯壳”里,注入树形结构的“灵魂”。这样做的好处太明显了:开发成本低,不需要引入新控件;用户体验无缝衔接,因为操作还是那个熟悉的表格;而且,DataGridView本身强大的数据绑定、单元格格式化、编辑功能,我们都能继续用,只是多了一个“展开/折叠”的魔法。
这个需求其实挺普遍的。除了我做的项目管理系统,像产品分类管理、多级菜单配置、带有分组属性的数据报表展示,但凡数据存在“父子关系”、“归属关系”的场景,这个技巧都能派上用场。它解决的,就是在有限的空间内,清晰、动态地展示复杂层级数据的核心痛点。
2. 核心思路:把单元格变成“树节点”
想要实现树形效果,最关键的一步是改变我们对DataGridView单元格的认知。在标准表格里,每个单元格是独立的,它们只关心自己的值和样式。而我们要做的,是让第一列的单元格“活”起来,让它知道自己是谁的儿子,谁又是它的孩子,并且能根据状态(展开或收起)来指挥其他行的显示与隐藏。
原始文章里给出的方案,其核心就是自定义单元格(DataGridViewGroupCell)。这不是简单的样式修改,而是从DataGridViewTextBoxCell继承,然后重写其内部逻辑,给它加上“家族关系”的属性。我来给你拆解一下这个自定义单元格里最重要的几个“家庭成员”:
GroupLevel(组级别):这个属性就像家族的辈分。根节点是1级,它的直接子节点是2级,孙节点是3级,以此类推。这个级别直接决定了单元格前面缩进的距离,级别越高,缩进越多,视觉上的层级感就越强。ParentCell(父节点):这是一个指向另一个DataGridViewGroupCell的引用。它明确地告诉当前单元格:“你的爸爸是谁”。通过这个属性,所有的单元格就能串联成一棵家族树。设置父节点时,父节点会自动把这个新单元格加入到自己的“孩子列表”里,这个设计很巧妙,保证了关系的双向绑定。ChildCells(子节点列表):这是一个List<DataGridViewGroupCell>,用来存放当前节点所有的直接子节点。当节点被折叠时,我们就遍历这个列表,把所有子节点所在的行隐藏起来;展开时,再让它们显示。这是实现折叠效果的数据基础。Collapsed(是否收起):一个简单的布尔值,记录了当前节点的状态。它是展开(false)还是收起(true)?这个状态决定了它前面绘制的是“+”号(可展开)还是“-”号(可收起),也决定了它的孩子们是否可见。



被折叠的 条评论
为什么被折叠?



