初学者:在 Winforms 中带有复选框和递归的 TreeView
Beginner: TreeView with checkboxes and recursion in Winforms
首先 post 和一个新的编码器,如果您需要比我提供的更多的信息,请与我联系。我正在尝试在层次结构中创建一个带有复选框的树视图(见图)。我的问题是我想创建某种递归,在检查父节点时取消选择并选择子节点,反之亦然。
我正在将 VS 与 winforms 一起使用,并在谷歌上搜索了 2 天以了解如何执行此操作,不幸的是,在线示例对我来说太高级或不起作用。我发现 Tutorial 关于如何使用不确定的复选框完全做到这一点,这将是一个很大的好处,但它适用于 WPF。
我设法创建了能够(取消)检查所有按钮的按钮,并提供了一些在线示例。请有人指导,一个到目前为止发现编程令人惊叹的初学者,在正确的方向:)
private void button_checkAllNodes_Click(object sender, EventArgs e)
{
checkAllNodes(treeView1.Nodes);
}
private void button_uncheckAllNodes_Click(object sender, EventArgs e)
{
UncheckAllNodes(treeView1.Nodes);
}
public void checkAllNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = true;
checkChildren(node, true);
}
}
public void UncheckAllNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = false;
checkChildren(node, false);
}
}
private void checkChildren(TreeNode rootNode, bool isChecked)
{
foreach (TreeNode node in rootNode.Nodes)
{
checkChildren(node, isChecked);
node.Checked = isChecked;
}
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
}
Picture Treeview with (un)check All buttons
我同意@jdweng 的观点,您在 checkChildren() 中使用了递归。缺少基本案例。
在递归 checkChildren 中,在
之前添加基本情况
foreach (TreeNode node in rootNode.Nodes)
if node is Null : rootNode=isChecked
让我们为 TreeNode
类型创建几个扩展方法,一个获取一个节点的所有子节点,另一个获取它的父节点。
// Within your project's namespace...
static class TreeViewExtensions
{
public static IEnumerable<TreeNode> Children(this TreeNode node)
{
foreach (TreeNode n in node.Nodes)
{
yield return n;
foreach (TreeNode child in Children(n))
yield return child;
}
}
public static IEnumerable<TreeNode> Parents(this TreeNode node)
{
var p = node.Parent;
while (p != null)
{
yield return p;
p = p.Parent;
}
}
}
现在,您需要做的就是处理 TreeView.AfterCheck
事件以切换扩展方法产生的节点的 Checked
属性。
// +
using System.Linq;
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action == TreeViewAction.Unknown) return;
foreach (TreeNode n in e.Node.Children())
n.Checked = e.Node.Checked;
// Comment this if you don't need it.
foreach (TreeNode p in e.Node.Parents())
p.Checked = p.Nodes.OfType<TreeNode>().Any(n => n.Checked);
}
很快,您会注意到有时当您快速单击复选框时此解决方案无法正常工作,因为默认情况下它们不会收到鼠标双击消息。然后,按照 post or 来解决这个问题。现在慢慢点击。
如果您更喜欢使用按钮来切换检查状态,请删除 AfterCheck
处理程序并改为:
private void btnCheckAll_Click(object sender, EventArgs e)
{
ToggleCheck(treeView1.SelectedNode, true);
}
private void btnUncheckAll_Click(object sender, EventArgs e)
{
ToggleCheck(treeView1.SelectedNode, false);
}
private void ToggleCheck(TreeNode node, bool state)
{
node.Checked = state;
foreach (TreeNode n in node.Children())
n.Checked = state;
// Optional...
foreach (TreeNode n in node.Parents())
n.Checked = state;
}
首先 post 和一个新的编码器,如果您需要比我提供的更多的信息,请与我联系。我正在尝试在层次结构中创建一个带有复选框的树视图(见图)。我的问题是我想创建某种递归,在检查父节点时取消选择并选择子节点,反之亦然。
我正在将 VS 与 winforms 一起使用,并在谷歌上搜索了 2 天以了解如何执行此操作,不幸的是,在线示例对我来说太高级或不起作用。我发现 Tutorial 关于如何使用不确定的复选框完全做到这一点,这将是一个很大的好处,但它适用于 WPF。
我设法创建了能够(取消)检查所有按钮的按钮,并提供了一些在线示例。请有人指导,一个到目前为止发现编程令人惊叹的初学者,在正确的方向:)
private void button_checkAllNodes_Click(object sender, EventArgs e)
{
checkAllNodes(treeView1.Nodes);
}
private void button_uncheckAllNodes_Click(object sender, EventArgs e)
{
UncheckAllNodes(treeView1.Nodes);
}
public void checkAllNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = true;
checkChildren(node, true);
}
}
public void UncheckAllNodes(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
node.Checked = false;
checkChildren(node, false);
}
}
private void checkChildren(TreeNode rootNode, bool isChecked)
{
foreach (TreeNode node in rootNode.Nodes)
{
checkChildren(node, isChecked);
node.Checked = isChecked;
}
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
}
Picture Treeview with (un)check All buttons
我同意@jdweng 的观点,您在 checkChildren() 中使用了递归。缺少基本案例。
在递归 checkChildren 中,在
之前添加基本情况
foreach (TreeNode node in rootNode.Nodes)
if node is Null : rootNode=isChecked
让我们为 TreeNode
类型创建几个扩展方法,一个获取一个节点的所有子节点,另一个获取它的父节点。
// Within your project's namespace...
static class TreeViewExtensions
{
public static IEnumerable<TreeNode> Children(this TreeNode node)
{
foreach (TreeNode n in node.Nodes)
{
yield return n;
foreach (TreeNode child in Children(n))
yield return child;
}
}
public static IEnumerable<TreeNode> Parents(this TreeNode node)
{
var p = node.Parent;
while (p != null)
{
yield return p;
p = p.Parent;
}
}
}
现在,您需要做的就是处理 TreeView.AfterCheck
事件以切换扩展方法产生的节点的 Checked
属性。
// +
using System.Linq;
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
if (e.Action == TreeViewAction.Unknown) return;
foreach (TreeNode n in e.Node.Children())
n.Checked = e.Node.Checked;
// Comment this if you don't need it.
foreach (TreeNode p in e.Node.Parents())
p.Checked = p.Nodes.OfType<TreeNode>().Any(n => n.Checked);
}
很快,您会注意到有时当您快速单击复选框时此解决方案无法正常工作,因为默认情况下它们不会收到鼠标双击消息。然后,按照
如果您更喜欢使用按钮来切换检查状态,请删除 AfterCheck
处理程序并改为:
private void btnCheckAll_Click(object sender, EventArgs e)
{
ToggleCheck(treeView1.SelectedNode, true);
}
private void btnUncheckAll_Click(object sender, EventArgs e)
{
ToggleCheck(treeView1.SelectedNode, false);
}
private void ToggleCheck(TreeNode node, bool state)
{
node.Checked = state;
foreach (TreeNode n in node.Children())
n.Checked = state;
// Optional...
foreach (TreeNode n in node.Parents())
n.Checked = state;
}