折叠DataGridView的行

csharp 复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private readonly Image _plusImage = Resource.Plus;
        private readonly Image _minusImage = Resource.Minus;
        private readonly Image _blankImage = Resource.Blank;
        private readonly string _imageColumnName = "imageColumn";
        private readonly string _checkedColumnName = "checkedColumn";
        private readonly string _nameColumnName = "deptNameColumn";
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            List<DataItem> dataItems = new List<DataItem>()
            {
                new DataItem(){Expanded=true,Id=100,Name="门诊部"},
                new DataItem(){Id=101,Name="儿科门诊",Description="专为儿童服务,限制年龄"},
                new DataItem(){Id=102,Name="妇科门诊",Description="转为女性服务,限制性别"},
                new DataItem(){Id=103,Name="内科门诊",Description="内科"},
                new DataItem(){Expanded=true,Id=200,Name="住院部"},
                new DataItem(){Id=201,Name="泌尿科",Description="泌尿专科"},
                new DataItem(){Id=202,Name="骨伤科",Description="骨科专科"},
                new DataItem(){Id=203,Name="妇产科",Description="妇科+产科"},
                new DataItem(){Id=204,Name="消化科",Description="消化科"},
            };
            dataItemDataGridView.Rows.Clear();
            /*  foreach (DataItem dataItem in dataItems)
              {
                  int count = dataItemDataGridView.Rows.Count;
                  dataItemDataGridView.Rows.Insert(dataItemDataGridView.Rows.Count, new object[] { !dataItem.Expanded.HasValue ? _blankImage : dataItem.Expanded.Value ? _minusImage : _plusImage, dataItem.Checked, dataItem.Name, dataItem.Description, dataItem.Id });
                  dataItemDataGridView.Rows[count].Tag = dataItem;
                  dataItemDataGridView.Rows[count].Visible = dataItem.Visible;
              }*/
            foreach (DataItem dataItem in dataItems)
            {
                DataGridViewRow row = new DataGridViewRow();
                row.Cells.Add(new DataGridViewImageCell() { Value = dataItem.Expanded.HasValue ? (dataItem.Expanded.Value ? _minusImage : _plusImage) : _blankImage });
                row.Cells.Add(new DataGridViewCheckBoxCell() { Value = dataItem.Checked });
                row.Cells.Add(new DataGridViewTextBoxCell() { Value = dataItem.Name });
                row.Cells.Add(new DataGridViewTextBoxCell() { Value = dataItem.Description });
                row.Cells.Add(new DataGridViewTextBoxCell() { Value = dataItem.Id });
                row.Tag = dataItem;
                row.Visible = dataItem.Visible;
                dataItemDataGridView.Rows.Add(row);
            }
            dataItemDataGridView.CellContentClick += dataItemDataGridView_CellContentClick;
        }

        /// <summary>
        /// 实现折叠/展开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataItemDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex >= 0)
            {
                DataGridViewRowCollection rows = dataItemDataGridView.Rows;
                if (e.ColumnIndex == 0)
                {
                    Image image = rows[e.RowIndex].Cells[_imageColumnName].Value as Image;
                    bool isChecked = (bool)rows[e.RowIndex].Cells[_checkedColumnName].Value;
                    string name = rows[e.RowIndex].Cells[_nameColumnName].Value as string;
                    if (image == _plusImage)
                    {
                        dataItemDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _minusImage;
                        int index = e.RowIndex + 1;
                        while (index < rows.Count && rows[index].Cells[_imageColumnName].Value != _plusImage && rows[index].Cells[_imageColumnName].Value != _minusImage)
                        {
                            dataItemDataGridView.Rows[index].Visible = true;
                            index++;
                        }
                    }
                    else if (image == _minusImage)
                    {
                        dataItemDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _plusImage;
                        int index = e.RowIndex + 1;
                        while (index < rows.Count-1 && rows[index].Cells[_imageColumnName].Value != _plusImage && rows[index].Cells[_imageColumnName].Value != _minusImage)
                        {
                            dataItemDataGridView.Rows[index].Visible = false;
                            index++;
                        }
                    }
                }
                else if (e.ColumnIndex == 1)
                {
                    bool isChecked = !(bool)rows[e.RowIndex].Cells[_checkedColumnName].Value;
                    dataItemDataGridView.Rows[e.RowIndex].Cells[_checkedColumnName].Value = isChecked;
                    DataItem dataItem = dataItemDataGridView.Rows[e.RowIndex].Tag as DataItem;
                    dataItem.Checked = isChecked;
                    Image image = rows[e.RowIndex].Cells[_imageColumnName].Value as Image;
                    if (image == _plusImage || image == _minusImage)
                    {
                        int index = e.RowIndex + 1;
                        while (index < rows.Count && rows[index].Cells[_imageColumnName].Value != _plusImage && rows[index].Cells[_imageColumnName].Value != _minusImage)
                        {
                            dataItemDataGridView.Rows[index].Cells[_checkedColumnName].Value = isChecked;
                            dataItem = dataItemDataGridView.Rows[index].Tag as DataItem;
                            dataItem.Checked = isChecked;
                            index++;
                        }
                    }
                    else
                    {
                        bool isGroupAllChecked = true;
                        int index = e.RowIndex;
                        while (index < rows.Count && rows[index].Cells[_imageColumnName].Value != _plusImage && rows[index].Cells[_imageColumnName].Value != _minusImage)
                        {
                            if (!(bool)rows[index].Cells[_checkedColumnName].Value)
                                isGroupAllChecked = false;
                            index++;
                        }
                        index = e.RowIndex - 1;
                        while (index >= 0 && rows[index].Cells[_imageColumnName].Value != _plusImage && rows[index].Cells[_imageColumnName].Value != _minusImage)
                        {
                            if (!(bool)rows[index].Cells[_checkedColumnName].Value)
                                isGroupAllChecked = false;
                            index--;
                        }
                        if (index >= 0 && (bool)rows[index].Cells[_checkedColumnName].Value != isGroupAllChecked)
                            dataItemDataGridView.Rows[index].Cells[_checkedColumnName].Value = isGroupAllChecked;
                    }
                }
            }
        }
  
    } 
}
csharp 复制代码
   public class DataItem
    {
        public bool? Expanded { get; set; } //是否展开 null-代表子项,不可折叠/展开 true-展开 false-折叠(用于显示不同的图标)
        public bool Checked { get; set; } //是否勾选
        public long Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public bool Visible { get; set; } = true;
    }

UI设计:

第一列类型为DataGridViewImageColumn

第二列类型为DataGridViewCheckBoxColumn

其中 Resource.Plus、Resource.Minus、Resource.Blank为图标资源文件

效果图:

相关推荐
泛凡(Linyongui)12 小时前
基于 CW32L010 的智能厨卫调温旋钮方案--低成本 EC21 编码旋钮全解析
单片机·嵌入式硬件·c#
暴走约伯14 小时前
【VFX工具链源码拆解 02】告别机械劳动(上):一键统改百个子粒子颜色与批量重命名
3d·unity·c#·材质
信息安全失业大专人员15 小时前
每日了解一个智能体-WorkBuddy
开发语言·ai·c#·智能体
呆呆敲代码的小Y16 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
淡海水17 小时前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
椒颜皮皮虾྅20 小时前
让 C# 读懂整张图:DeploySharp 驱动 PaddleOCR v4/v5/v6,打通 OCR 全流程
开发语言·人工智能·深度学习·学习·c#·ocr·cuda
昂好的day20 小时前
C# 同步异步与多线程:Thread / Task / 线程池一次讲透(上篇)
开发语言·windows·c#
吃一碗粉只付一碗钱1 天前
12-给SPA友好的401和403响应
c#·状态模式
caimouse1 天前
MonoDevelop 编译用户应用程序时调用的是哪个编译器
c#
will_net2 天前
apollo 站点部署&C#接入
开发语言·c#·apollo