Markus Rechberger Ответов: 3

Дерево - перебрать все узлы, остановить на первый виден


У меня сейчас ментальный блок, это не может быть так сложно.
У меня есть функция, которая должна получить последний видимый узел в TreeView. Сначала я хочу перейти к первому видимому узлу, а затем ко второму оттуда идти до тех пор, пока узел не станет видимым.свойство IsVisible().

Что я уже пробовал:

public TreeNode GetLastVisibleNode()
    {
        var node = treeControl1.Nodes.Cast<TreeNode>().Where(x => x.IsVisible).FirstOrDefault();
        TreeNode retVal = node;
        while (node != null && node.IsVisible)
        {
            if (!node.IsSelected)
                retVal = node;
            node = node.NextVisibleNode;
        }
        return retVal;
    }


Я сделал это, чтобы выполнить итерации в последний видимый узел, когда я первый видимый (ура), но мой подход получения первых видимых неверно (оххххх!)
Я заметил, что treeControl1.Узлы дают мне только родительские узлы, но очевидно, что я хочу получить первый видимый узел из всех узлов.
Я также знаю, что мне, вероятно, нужен рекурсивный метод, но, как я уже сказал в самом начале, у меня сейчас есть ментальный блок, и я хочу, чтобы это было исправлено довольно скоро :(

3 Ответов

Рейтинг:
24

Eric Lynch

Должно сработать следующее:

private static TreeNode FindFirstVisible(TreeNodeCollection nodes)
{
  foreach (TreeNode node in nodes)
  {
    if (node.IsVisible)
      return node;

    TreeNode first = FindFirstVisible(node.Nodes);
    if (first != null)
      return first;
  }

  return null;
}

Для вызова используйте следующие функции:

TreeNode first = FindFirstVisible(tree.Nodes);

Для полноты картины вы также можете проверить (что немного отличается от того, что вы просите):
TreeView элемент.Свойство TopNode (System.Окна.Формы) | Microsoft Docs[^]

На самом деле, подумав немного дольше, вам даже не нужно будет копаться в дереве. Я считаю, что для того, чтобы дочерний узел был виден, должен быть виден и родитель. Итак, я думаю, что у вас либо должен быть видимый узел верхнего уровня, либо никаких узлов не видно. Итак, должно сработать следующее:

private static TreeNode FindFirstVisible(TreeNodeCollection nodes) =>
  nodes.Cast<TreeNode>().FirstOrDefault(node => node.IsVisible);

Основываясь на последующей информации, и если вы беспокоитесь о прокрутке, следующие методы позволят вам выбрать последний частично или полностью видимый узел. С помощью NextVisibleNode не будем рассматривать, вышел ли узел из диапазона прокрутки.
private TreeNode GetFirstVisibleNode(TreeView tree, bool includePartial)
{
  if (!includePartial)
    return tree.TopNode;

  Rectangle treeBounds = tree.ClientRectangle;
  return tree.Nodes.Cast<TreeNode>()
    .FirstOrDefault(node => node.IsVisible &&
      treeBounds.Contains(node.Bounds));
}

private TreeNode GetLastVisibleNode(TreeView tree, bool includePartial)
{
  TreeNode last = GetFirstVisibleNode(tree, includePartial);
  Rectangle treeBounds = tree.Bounds;

  for (TreeNode node = last; node != null; node = node.NextVisibleNode)
  {
    if (includePartial)
    {
      if (!treeBounds.IntersectsWith(node.Bounds))
        break;
    }
    else
    {
      if (!treeBounds.Contains(node.Bounds))
        break;
    }

    last = node;
  }

  return last;
}


[no name]

Во-первых: я случайно оценил ваш вопрос одной звездой :( мне очень жаль!

Второе: я нашел другое решение, которое работает очень хорошо, поэтому я не тестировал ваше

0x01AA

Вы можете легко исправить свой голос, нажав на 5-ю звезду :-)

Eric Lynch

Без проблем. Возможно, вы захотите прочитать ответ. Если вы ищете первый действительно видимый узел (IsVisible=true и не прокручивается вне поля зрения), то TreeView.TopNode-это лучший способ пойти. В противном случае не беспокойтесь о рекурсии, просто изучите узлы верхнего уровня, один из них гарантированно будет первым видимым узлом, если какие-либо узлы в дереве видны. Это простой фрагмент LINQ, представленный в конце моего решения. Кроме того, что касается голосования, я думаю, что вы можете отредактировать свой выбор...Я забыл. В любом случае, никаких проблем.

[no name]

Спасибо, что показали мне вид на дерево.Свойство TopNode! Я использовал это, чтобы добраться до первого, а затем в основном сделал то же самое, что и раньше (что вы можете видеть в моем вопросе) с немного другой семантикой.

Кроме того, теперь я мог редактировать свой голос (раньше не мог этого сделать, поэтому извинился). Я отмечу Ваш ответ как принятый, а также добавлю третий ответ с тем, что я сделал сейчас, который гораздо легче читать для меня, как новичка в C#.

Eric Lynch

Еще одна вещь, которую вы, возможно, захотите рассмотреть, узел может иметь IsVisible = true и все еще не быть видимым, потому что он прокручивается вне диапазона. К сожалению, NextVisibleNode не рассматривает прокрутку. Я обновил решение, включив в него методы, учитывающие прокрутку, на случай, если это входит в ваши требования.

Рейтинг:
18

Markus Rechberger

public TreeNode GetLastVisibleNode()
        {
            return treeControl1.Nodes.Cast<TreeNode>().Select(GetLastVisibleNode).
                            LastOrDefault(first => first != null);
        }

TreeNode GetLastVisibleNode(TreeNode parentNode) =>
        parentNode.IsVisible
             ? parentNode
             : parentNode.Nodes.Cast<TreeNode>().Select(GetLastVisibleNode).
                      LastOrDefault(childFirstNode => childFirstNode != null);

Кто-то ответил на тот же вопрос, что и я здесь


Рейтинг:
11

Markus Rechberger

Мне сказали в комментариях, что TreeView.TopNode дает мне первый видимый узел.

С этого момента я просто перебирал список до тех пор, пока не стал узлом.IsVisible и node != null.

public TreeNode GetLastVisibleNode()
        {
            TreeNode node = treeControl1.TopNode;
            TreeNode retVal;
            do
            {
                retVal = node;
                node = node.NextVisibleNode;
            } while (node != null && node.IsVisible);

            return retVal;
        }