первый пост и новый кодер, так что будьте со мной, если вам нужно больше информации, чем я даю. Я пытаюсь создать древовидную структуру с флажками в иерархии (см. рис.). Моя проблема в том, что я хочу создать какую-то рекурсию, которая отменяет выбор и выбирает дочерние узлы, когда проверяются родительские узлы, или наоборот.
Я использую VS с winforms и уже 2 дня гуглю, как это сделать, к сожалению, онлайн-примеры либо слишком сложны для меня, либо не работают. Я нашел Учебник о том, как сделать это именно с неопределенными флажками, что было бы большим бонусом, но это для 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)
{
}
Я согласен с @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;
}
Эй, большое спасибо за это. Почти делает именно то, что я хочу. Извиняюсь за поздний ответ, так как был в отпуске. Как мне сделать так, чтобы он проверял/снимал галочку на лету? Вместо того, чтобы делать это кнопкой на выбранных узлах?
@Breakwin С возвращением. Если вы проверите первую версию моего ответа, вы увидите эту часть. Я думал, что вам это не нужно, поэтому я удалил его. Я добавлю это снова.
блестяще, это получилось. большое спасибо. Мне просто пришлось добавить событие aftercheck дополнительно. По-видимому, это не было добавлено в древовидную структуру, когда я ее создал. learn.microsoft.com/en-us/dotnet/api/…
Не уверен, в чем ваша проблема. Единственное, что я вижу неправильно, это когда rootNode.Nodes имеет значение null. Поэтому вам нужен оператор IF для возврата из CheckAllNodes/UnCheckAllNodes, когда rootNode.Nodes имеет значение null.