因为项目需要,需要生成一个TreeView,具体的要求是:实现每个节点有:勾选(所有子节点勾选)、不勾选(所有子节点不勾选)、部分勾选(部分子节点勾选),三种状态。同时,还要能禁用不勾选节点,只能显示,不能勾选。(这里没有做禁用可勾选节点,因为项目不需要~,大家也可以按照我这个思路去实现)
查了很多资料,下了很多代码,发现没有能够完全实现上述的方法,有人说用TreeViewde的icon来实现,但是这样就相当于把TreeeView的check事件给阉割抛弃了,只能用select事件,同时要勾选某个节点必须要选中这个节点才能实现,这样的用户体验是不够好的。
自己决定撸起袖子做一个,作为一个接触c#不到两周的人,不足之处,批评指正。
整个实现方案,参考了以下两位博主的内容:
整体思路
重画TreeView的CheckBox,为了省事,采取覆盖的方式去重画,因此,本方法目前只适合win7以上系统界面,xp上会……不太好看,因为xp的复选框太大,会盖不住,下步会增加改进
1.利用每个TreeNode的ToolTips对每个节点的状态进行定义
2.ToolTips的值为1:勾选、2:不选、3:部分勾选
3.当TreeNode.foreColor为Gray时,灰色,代表这个节点被禁用
4.当然也可以用TreeNode的name、Tag属性进行定义,由于我的项目都用到了,不得不用ToolTips进行定义。
5.重画treeView的复选框,画复选框时,先判断foreColor是否是灰色,是灰色则画禁用框,不是则根据TreeNode的ToolTips来画形态。
到此,一个三态加禁用不可勾选的的TreeView就定义好了,在需要设置的时候,通过设置TreeNode的foreColor和ToolTips即可 。
下面设置当一个节点被勾选或勾除时,上下联动的机制
这个机制我专门定义了一个静态类来操作TreeViewHandler
当一个节点被勾选时,需要改变它本身、所有子节点、所有父节点的状态,
1. 本身:
a) 如果本身不是被禁用节点则打钩
b) 如果所有子节点中有禁用节点,则本身改成部分选中
2. 子节点:
a) 方法同本身
3. 父节点
a) 判断本身的所有兄弟节点的状态
b) 都勾选,则父节点为勾选
c) 否则,则父节点为部分勾选
当一个节点被勾除时
1. 本身:
a) 变成勾除状态
2. 子节点
a) 都变成勾除状态
3. 父节点
a) 判断本身所有兄弟节点的状态
b) 都勾除,则父节点为勾除
c) 否则,父节点为部分勾除
由于节点在做联动时,并不能百分之百引发TreeView的重画机制,(当节点在勾选状态变成部分勾选状态时就不会,双击某个节点复选框时,节点的重画机制就会延迟),所以专门定义了一个方法setNodeState()做节点的勾选与勾除,主要做法实际是将这个节点反复勾一次,确保系统对其进行重画。
最后附上代码:
具体代码
MyTreeView.cs
namespace TreeView
{
enum CheckState
{
选中,
不勾选,
部分被选,
禁用
}
/// <summary>
/// 本类复写TreeView,实现了复选框的三态功能,且每个节点能够被禁用
/// 使用方法:
/// 勾选一个节点,将其ToolTipsText 设置为“1”
/// 取消勾选一个节点,将其ToolTipsText 设置为“2”
/// 部分勾选一个节点,将其ToolTipsText 设置为“3”
/// 禁用某个节点(不勾选),将其ForeColor设置为 Color.gray
/// </summary>
public class MyTreeView : System.Windows.Forms.TreeView
{
private Brush b = null;//节点字体颜色
private Point p;//画CheckBox的位置
public MyTreeView()
{
this.DrawMode = TreeViewDrawMode.OwnerDrawText;//自己画文本
}
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
b = Brushes.Black;//默认字体为黑色
if (e.Node.ForeColor == Color.Gray)
{
drawCheckBox(CheckState.禁用, e);
}
else//画可选择框
{
switch (e.Node.ToolTipText)
{
case ("1" ) :
drawCheckBox(CheckState.选中, e);
break;
case "2":
drawCheckBox(CheckState.不勾选, e);
break