今天我在试着做一个C#树形视图(TreeView)控件,要求在每个节点前面添加一个可用于打勾的复选框,并要求复选框有上下级联动的效果。现在在网上能查到挺多满足这类功能的代码,原本我也以为这是一件挺简单的事情,不过实际情况并非如此。
我们建立一个C#窗体应用程序,主窗体取名FormMain,在里面放置一个Dock为Fill的TreeView控件treeTest。注意该控件的CheckBoxes属性要设置为True才能显示复选框。
在FormMain中写入代码如下:
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 TreeViewCheckTest
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
}
private void FormMain_Load(object sender, EventArgs e)
{
//生成测试数据
TreeNode treeNode11 = new TreeNode("蜉蝣目");
treeNode11.Nodes.Add("等蜉科");
treeNode11.Nodes.Add("四节蜉科");
treeNode11.Nodes.Add("扁蜉科");
treeNode11.Nodes.Add("蜉蝣科");
treeNode11.Nodes.Add("河花蜉科");
TreeNode treeNode12 = new TreeNode("蜚蠊目");
treeNode12.Nodes.Add("姬蠊科");
treeNode12.Nodes.Add("硕蠊科");
treeNode12.Nodes.Add("地鳖科");
treeNode12.Nodes.Add("隐尾蠊科");
TreeNode treeNode13 = new TreeNode("螳螂目");
treeNode13.Nodes.Add("螳科");
treeNode13.Nodes.Add("花螳科");
treeNode13.Nodes.Add("锥头螳科");
treeNode13.Nodes.Add("细足螳科");
TreeNode treeNode14 = new TreeNode("其他类型昆虫");
TreeNode treeNode1 = new TreeNode("昆虫纲");
treeNode1.Nodes.AddRange(
new TreeNode[] { treeNode11, treeNode12, treeNode13, treeNode14 });
treeTest.Nodes.Add(treeNode1);
treeTest.ExpandAll();
}
/// <summary>
/// 关联锁
/// </summary>
bool needSetRelateCheck = true;
/// <summary>
/// 树节点前复选框发生变化时触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void treeViewEnhanced1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (!needSetRelateCheck)
{
return;
}
//1、判断当前操作节点勾还是不勾
//如果勾,当前节点下所有子节点都要勾上
//如果不勾,下面子节点全部不勾
TreeNode node = e.Node;
if (node.Checked)
{
node.Nodes.OfType<TreeNode>().ToList().ForEach(x => x.Checked = true);
}
else
{
node.Nodes.OfType<TreeNode>().ToList().ForEach(x => x.Checked = false);
}
//2、如果当前节点被勾选,如果当前节点被勾选,则其上溯所有祖先节点都要勾
//否则判断当前节点所有兄弟节点是否有勾,有则父节点要勾,没有则父节点不勾
needSetRelateCheck = false; //为本方法上锁,确保连带影响不会运行本事件中代码
if (node.Checked)
{
while (node.Parent != null)
{
node = node.Parent;
node.Checked = true;
}
}
else
{
while (node.Parent != null)
{
node = node.Parent;
bool hasCheckedChild = false;
foreach (TreeNode child in node.Nodes)
{
if (child.GetHashCode() == e.Node.GetHashCode())
{
continue;
}
if (child.Checked)
{
hasCheckedChild = true;
break;
}
}
if (!hasCheckedChild)
{
node.Checked = false;
}
else
{
break;
}
}
}
needSetRelateCheck = true; //为本方法解锁
}
}
}
原则上这段代码满足以下功能:
1:勾选一个节点时,该节点的所有子节点都被勾选
2:取消勾选一个节点时,该节点的所有子节点都被取消勾选
3:勾选一个节点时,如果该节点的所有兄弟都被勾选,则该节点的父节点也应被勾选
4:取消勾选一个节点时,如果该节点的所有兄弟节点都未被勾选,则该节点的父节点也应被取消勾选
运行后效果如下:
本来以为这样就好了,结果发生了意想不到的事情:
在我用鼠标点击一个复选框时,如果点击间隔时间过短(达到类似双击的速度),则TreeView的复选框会显示出现无法正确联动的问题。
后来我上网查了一些资料,终于找到了一个大牛给出的原因,参见:
https://social.msdn.microsoft.com/Forums/windows/en-US/9d717ce0-ec6b-4758-a357-6bb55591f956/possible-bug-in-net-treeview-treenode-checked-state-inconsistent?forum=winforms
大牛的答案中说这是一个WindowsVista版本的BUG,不过我现在的32位Win7也会出现此问题。
解决这个问题的方法是将TreeView封装一层,并重写一下WndProc函数:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TreeViewCheckTest
{
/*
此种重写此函数,会导致鼠标触发的时候变的缓慢,
*/
class TreeViewEnhanced : TreeView
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x203) { m.Result = IntPtr.Zero; }
else base.WndProc(ref m);
}
}
//改进方法(用此类控件代替将响应变的更快)
public class TreeViewEx : TreeView
{
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_RBUTTONDOWN = 0x0204;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_LBUTTONDBLCLK)
{
TreeViewHitTestInfo tvhti = HitTest(new Point((int)m.LParam));
if (tvhti != null && tvhti.Location == TreeViewHitTestLocations.StateImage)
{
m.Result = IntPtr.Zero;
tvhti.Node.Checked = !tvhti.Node.Checked;
return;
}
}
else if (m.Msg == WM_RBUTTONDOWN)
{
TreeViewHitTestInfo tvhti = HitTest(new Point((int)m.LParam));
if (tvhti != null)
this.SelectedNode = tvhti.Node;
}
base.WndProc(ref m);
}
}
实现了TreeViewEnhanced类后,将FormMain中的TreeView控件替换为我们刚刚实现的TreeViewEnhanced就好啦!
转载:https://blog.csdn.net/weixin_34364135/article/details/92548715
转载于:https://my.oschina.net/Tsybius2014/blog/551358