如何在 C# 的 TreeView 中显示多个复选框?

How to show multiple check boxes in a TreeView in C#?

我知道如何为 TreeView 中的每个 TreeNode 显示一个复选框。 我想在 TreeView 中为每个 TreeNode 显示 3 个复选框。 这样做的原因是我的程序中有3个图表,每个TreeNode代表一个不同的系列。我想让用户可以选择在他们喜欢的任何图表上显示每个系列。

这(或类似的东西)可能吗?提前致谢。

您需要自己绘制 TreeView。不完全是所有者绘制控件的最简单示例,但仍然不太难。

这是一个示例屏幕截图:

首先我们创建简单的 TreeNode 子类来保存额外的数据:

public class TreeNode3 : TreeNode
{
    public string Label { get; set; }
    public bool Check1 { get; set; }
    public bool Check2 { get; set; }
    public bool Check3 { get; set; }

    public new string Text
    {
        get { return Label; }
        set { Label = value; base.Text = ""; }
    }

    public TreeNode3()   { }

    public TreeNode3(string text)   { Label = text; }

    public TreeNode3(string text, bool check1, bool check2, bool check3)
    {
        Label = text;
        Check1 = check1; Check2 = check2; Check3 = check3;
    }

    public TreeNode3(string text, TreeNode3[] children)
    {
        Label = text;
        foreach (TreeNode3 node in children) this.Nodes.Add(node);
    }
}

注意,我隐藏了原来的Text,基本上用一个字符串变量Label代替了。我避免使用 Tag 所以你仍然可以自己使用它。我还没有实现所有的构造函数。如果您需要 ImageLists,您可能需要添加 ImageIndices.

现在 TreeView 本身:

using System.Windows.Forms.VisualStyles;
//..

public partial class UcTreeView : TreeView
{
    [DisplayName("Checkbox Spacing"),  CategoryAttribute("Appearance"),
     Description("Number of pixels between the checkboxes.")]
    public int Spacing { get; set; }

    [DisplayName("Text Padding"), CategoryAttribute("Appearance"), 
     Description("Left padding of text.")]
    public int LeftPadding { get; set; }



    public UcTreeView()
    {
        InitializeComponent();
        DrawMode = TreeViewDrawMode.OwnerDrawText;
        HideSelection = false;    // I like that better
        CheckBoxes = false;       // necessary!
        FullRowSelect = false;    // necessary!
        Spacing = 4;              // default checkbox spacing
        LeftPadding = 7;          // default text padding
    }

    public TreeNode3 AddNode(string label, bool check1, bool check2, bool check3)
    {
        TreeNode3 node = new TreeNode3(label, check1, check2, check3);
        this.Nodes.Add(node);
        return node;
    }

    private  Size glyph = Size.Empty;

    protected override void OnDrawNode(DrawTreeNodeEventArgs e)
    {
        TreeNode3 n = e.Node as TreeNode3;
        if (n == null) { e.DrawDefault = true; return; }

        CheckBoxState cbsTrue = CheckBoxState.CheckedNormal;
        CheckBoxState cbsFalse = CheckBoxState.UncheckedNormal;

        Rectangle rect = new Rectangle(e.Bounds.Location, 
                             new Size(ClientSize.Width, e.Bounds.Height));
        glyph = CheckBoxRenderer.GetGlyphSize(e.Graphics, cbsTrue );
        int offset = glyph.Width * 3 + Spacing * 2 + LeftPadding;

        if (n.IsSelected)
        {
            e.Graphics.FillRectangle(SystemBrushes.MenuHighlight ,rect);
            e.Graphics.DrawString(n.Label, Font, Brushes.White, 
                                  e.Bounds.X + offset, e.Bounds.Y);
        }
        else
        {
            CheckBoxRenderer.DrawParentBackground(e.Graphics, e.Bounds, this);
            e.Graphics.DrawString(n.Label, Font, Brushes.Black, 
                                  e.Bounds.X + offset, e.Bounds.Y);
        }

        CheckBoxState bs1 = n.Check1 ? cbsTrue : cbsFalse;
        CheckBoxState bs2 = n.Check2 ? cbsTrue : cbsFalse;
        CheckBoxState bs3 = n.Check3 ? cbsTrue : cbsFalse;

        CheckBoxRenderer.DrawCheckBox(e.Graphics,  cbx(e.Bounds, 0).Location, bs1);
        CheckBoxRenderer.DrawCheckBox(e.Graphics,  cbx(e.Bounds, 1).Location, bs2);
        CheckBoxRenderer.DrawCheckBox(e.Graphics,  cbx(e.Bounds, 2).Location, bs3);

    }

    protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
    {
        Console.WriteLine(e.Location + " bounds:"  + e.Node.Bounds);

        TreeNode3 n = e.Node as TreeNode3;
        if (e == null) return;

        if      (cbx(n.Bounds, 0).Contains(e.Location)) n.Check1 = !n.Check1;
        else if (cbx(n.Bounds, 1).Contains(e.Location)) n.Check2 = !n.Check2;
        else if (cbx(n.Bounds, 2).Contains(e.Location)) n.Check3 = !n.Check3;
        else
        {
            if (SelectedNode == n && Control.ModifierKeys == Keys.Control)
                 SelectedNode = SelectedNode != null ? null : n;
            else SelectedNode = n;
        }

        Console.WriteLine(" " + n.Check1 + " " + n.Check2 +" " + n.Check3 );

        Invalidate();

    }


    Rectangle cbx(Rectangle bounds, int check)
    {
        return new Rectangle(bounds.Left + 2 + (glyph.Width + Spacing) * check, 
                             bounds.Y + 2, glyph.Width, glyph.Height);
    }

}

一些注意事项:

  • 树不支持 LabelEditing。可行,但又是一堆蠕虫..
  • 树不支持 FullrowSelect 是真的,但实际上它仍然有效..
  • 您应该可以使用 ImageListStateImageList 但需要在添加节点后设置索引。
  • 不要将 CheckBoxes 属性 设置为 true!所有三个 CheckBoxes 都是虚拟的,您可以在将节点转换为 TreeNode3 作为其 Check1..Check3 属性后访问它们!
  • 我还没有实现三态复选框。您可以将布尔值更改为可为空,然后在单击和绘制事件中添加所需的代码。
  • 我在代码中留下了一些Console.WriteLines以便更好地测试..

Update 我添加了 SpacingPadding 属性 以及一些构造函数。 现在设置示例的表单代码看起来很正常:

ucTreeView1.Nodes.Add(new TreeNode3("Bauhaus", true, true, false));
TreeNode3 aNode = ucTreeView1.AddNode("Beatles", true, true, false);
ucTreeView1.Nodes.Add(new TreeNode3("Blur", true, true, false));
ucTreeView1.Nodes.Add(new TreeNode3("Byrds", true, true, false));
ucTreeView1.Nodes.Add(new TreeNode3("Bee Gees", new TreeNode3[]{
    new TreeNode3("Barry", true, false, false),
    new TreeNode3("Robin"),  
    new TreeNode3("Maurice")} ));

TreeNode3 aNodeA = new TreeNode3("John",   true, true,  false);
TreeNode3 aNodeB = new TreeNode3("Paul",   true, true,  true);
TreeNode3 aNodeC = new TreeNode3("George", true, false, true);
TreeNode3 aNodeD = new TreeNode3("Ringo",  true, false, false);

aNode.Nodes.Add(aNodeA);
aNode.Nodes.Add(aNodeB);
aNode.Nodes.Add(aNodeC);
aNode.Nodes.Add(aNodeD);
I have applied treeview nodes populated with multiple childs in one of my work, hence it can give you an idea: 
int k=0,L=0,totalNode=0;
            for (int j = 0; j < ((object[])(row[0, 0])).Length; j++) {

                TreeNode root = new TreeNode();  // Creating new root node
                root.Text = ((object[])(row[0, 1]))[j].ToString()+" "; 
                root.Tag = ((object[])(row[0, 0]))[j].ToString();
                int projectID = Convert.ToInt32(root.Tag);
                treeView1.Nodes.Add(root); //Adding the node
                totalNode++;

                TaskDataHandler taskData = new TaskDataHandler();
                object[,] arrTask = new object[1, 2];
                arrTask = taskData.GetTaskData(xdoc, AppVariable.apiToken, projectID);
                for (int i = 0; i < ((object[])(arrTask[0, 0])).Length; i++) {
                    totalNode++;
                    TreeNode child = new TreeNode(); // creating child node
                    child.Text = ((object[])(arrTask[0, 1]))[i].ToString() + " ";
                    child.Tag = ((object[])(arrTask[0, 0]))[i].ToString();
                    root.Nodes.Add(child); // adding child node
                }

            }

按照此 link 了解如何在 link 的节点中添加复选框: https://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.checkboxes%28v=vs.140%29.aspx