如何在 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
是真的,但实际上它仍然有效..
- 您应该可以使用
ImageList
和 StateImageList
但需要在添加节点后设置索引。
- 不要将
CheckBoxes
属性 设置为 true!所有三个 CheckBoxes
都是虚拟的,您可以在将节点转换为 TreeNode3
作为其 Check1..Check3
属性后访问它们!
- 我还没有实现三态复选框。您可以将布尔值更改为可为空,然后在单击和绘制事件中添加所需的代码。
- 我在代码中留下了一些
Console.WriteLines
以便更好地测试..
Update 我添加了 Spacing
和 Padding
属性 以及一些构造函数。
现在设置示例的表单代码看起来很正常:
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
我知道如何为 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
是真的,但实际上它仍然有效.. - 您应该可以使用
ImageList
和StateImageList
但需要在添加节点后设置索引。 - 不要将
CheckBoxes
属性 设置为 true!所有三个CheckBoxes
都是虚拟的,您可以在将节点转换为TreeNode3
作为其Check1..Check3
属性后访问它们! - 我还没有实现三态复选框。您可以将布尔值更改为可为空,然后在单击和绘制事件中添加所需的代码。
- 我在代码中留下了一些
Console.WriteLines
以便更好地测试..
Update 我添加了 Spacing
和 Padding
属性 以及一些构造函数。
现在设置示例的表单代码看起来很正常:
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