效果图,源码下载地址: https://pan.baidu.com/s/1BbaUtzqMsKNj3PtT6IsncQ?pwd=q245 提取码: q245
核心代码
using System; using System.Data; using System.Drawing; using System.Windows.Forms; namespace TreeViewDemo { public class TreeViewEnhanced : TreeView { protected override void WndProc(ref Message m) { this.DrawMode = TreeViewDrawMode.OwnerDrawText; if (m.Msg == 0x203) { m.Result = IntPtr.Zero; } else base.WndProc(ref m); } public TreeViewEnhanced() { this.DrawNode += new DrawTreeNodeEventHandler(this.treeViewEnhanced1_DrawNode); this.NodeMouseClick += new TreeNodeMouseClickEventHandler(this.treeViewEnhanced1_NodeMouseClick); this.AfterCheck += new TreeViewEventHandler(this.treeViewEnhanced1_AfterCheck); this.BeforeSelect += new TreeViewCancelEventHandler(this.treeViewEnhanced1_BeforeSelect); this.CheckBoxes = true; } /// <summary> /// 绑定TreeView(利用TreeNodeCollection) /// </summary> /// <param name="tnc">TreeNodeCollection(TreeView的节点集合)</param> /// <param name="pid">父id的值</param> /// <param name="idColName">数据库 id 字段名</param> /// <param name="pidColName">数据库 父id 字段名</param> /// <param name="idValColName">数据库 文本 字段名</param> /// <param name="enabled">是否启用 字段名</param> public void Bind_Tv(DataTable dt, TreeNodeCollection tnc, int pid, string idColName, string pidColName, string idValColName, string enabled) { DataView dv = new DataView(dt);//将DataTable存到DataView中,以便于筛选数据 TreeNode tn;//建立TreeView的节点(TreeNode),以便将取出的数据添加到节点中 //构建“父id字段=父id字段值”的查询条件 string filter = $"{pidColName}={pid}"; dv.RowFilter = filter;//利用DataView将数据进行筛选,选出相同 父id值 的数据 foreach (DataRowView drv in dv) { tn = new TreeNode();//建立一个新节点(学名叫:一个实例) tn.Name = drv[idColName].ToString();//节点的Value值,一般为数据库的id值 tn.Text = drv[idValColName].ToString();//节点的Text,节点的文本显示 tn.Tag = drv[enabled]; tnc.Add(tn);//将该节点加入到TreeNodeCollection(节点集合)中 int pidVal; if (int.TryParse(tn.Name, out pidVal)) { Bind_Tv(dt, tn.Nodes, pidVal, idColName, pidColName, idValColName, enabled);//递归(反复调用这个方法,直到把数据取完为止) } } } private void treeViewEnhanced1_DrawNode(object sender, DrawTreeNodeEventArgs e) { TreeNode treeNode = e.Node; TreeView treeview = sender as TreeView; var brush = Brushes.Black; if ("0".Equals(treeNode.Tag.ToString())) { brush = Brushes.Gray; treeNode.Checked = false; } if (treeNode != null && treeNode.Nodes.Count > 0) { if ("0".Equals(treeNode.Tag.ToString())) { foreach (TreeNode node in treeNode.Nodes) { node.Tag = 0; node.Checked = false; } } else { int isCheckedCount = 0, isNoCheckedCount = 0; foreach (TreeNode node in treeNode.Nodes) { if (node.Checked) { isCheckedCount++; } else { isNoCheckedCount++; } } if (isCheckedCount > 0 && isNoCheckedCount > 0) { var location = e.Node.Bounds.Location; location.Offset(-11, 2); var size = new Size(9, 9); //brush = Brushes.Blue; e.Graphics.FillRectangle(brush, new Rectangle(location, size)); } else if (isCheckedCount > 0) { treeNode.Checked = true; } else if (isNoCheckedCount > 0) { treeNode.Checked = false; } } } //绘制文本 e.Graphics.DrawString(e.Node.Text, treeview.Font, brush, e.Bounds.Left, e.Bounds.Top); } private void treeViewEnhanced1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { TreeView treeView = sender as TreeView; if (treeView != null) { treeView.Refresh(); } } private void treeViewEnhanced1_AfterCheck(object sender, TreeViewEventArgs e) { //只处理鼠标点击引起的状态变化 if (e.Action == TreeViewAction.ByMouse) { //更新子节点状态 UpdateChildNodes(e.Node); } } private void UpdateChildNodes(TreeNode node) { bool state = node.Checked; foreach (TreeNode child in node.Nodes) { child.Checked = state; UpdateChildNodes(child); } } private void treeViewEnhanced1_BeforeSelect(object sender, TreeViewCancelEventArgs e) { if ("0".Equals(e.Node.Tag.ToString())) e.Cancel = true; } } }
添加到treeView节点的数据TestData.json
[ { "id": 0, "idVal": "一般检查", "pid": -1, "enabled": 1 }, { "id": 1, "idVal": "外观检查", "pid": 0, "enabled": 1 }, { "id": 2, "idVal": "标志检查", "pid": 0, "enabled": 0 }, { "id": 3, "idVal": "尺寸检查", "pid": 0, "enabled": 1 }, { "id": 4, "idVal": "基本构成检查", "pid": 0, "enabled": 1 }, { "id": 5, "idVal": "充电模式和连接方式检查", "pid": 0, "enabled": 1 }, { "id": 6, "idVal": "充电连接装置检查", "pid": 0, "enabled": 1 }, { "id": 7, "idVal": "电缆管理及储存检查", "pid": 0, "enabled": 1 }, { "id": 8, "idVal": "机械开关设备检查", "pid": 0, "enabled": 1 }, { "id": 9, "idVal": "防雷措施检查", "pid": 0, "enabled": 1 }, { "id": 10, "idVal": "防盗措施检查", "pid": 0, "enabled": 1 }, { "id": 11, "idVal": "通用器件符合性检查", "pid": 0, "enabled": 1 }, { "id": 12, "idVal": "专用部件符合性检查", "pid": 0, "enabled": 1 } ]
将数据添加到TreeView和自动切换节点
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { string testData = File.ReadAllText("testData.json"); DataTable dt = JsonConvert.DeserializeObject<DataTable>(testData); treeViewEnhanced1.Bind_Tv(dt, treeViewEnhanced1.Nodes, -1, "id", "pid", "idVal", "enabled"); treeViewEnhanced1.ExpandAll(); treeViewEnhanced1.Refresh(); } Thread th; private void button1_Click(object sender, EventArgs e) { if (th == null || !th.IsAlive) { th = new Thread(Test); th.IsBackground = true; th.Start(); } } private void Test() { foreach (TreeNode tn in treeViewEnhanced1.Nodes[0].Nodes) { if (tn.Checked) { this.BeginInvoke(new Action(() => { treeViewEnhanced1.Focus(); treeViewEnhanced1.SelectedNode = tn; label1.Text = $"正在测试【{tn.Text}】"; })); Thread.Sleep(800); } } this.BeginInvoke(new Action(() => { label1.Text = "测试完成"; MessageBox.Show("测试完成"); })); } }
本文来自 www.luofenming.com