The following should work:
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;
}
To call, use as follows:
TreeNode first = FindFirstVisible(tree.Nodes);
For completeness, you may also want to check out (which is slightly different from what you ask):
TreeView.TopNode Property (System.Windows.Forms) | Microsoft Docs[
^]
Actually, thinking a bit longer, you shouldn't even need to dig into the tree. I believe for a child node to be visible the parent must be visible. So, I think you either must have a top level node visible, or no nodes are visible. So, the following should work:
private static TreeNode FindFirstVisible(TreeNodeCollection nodes) =>
nodes.Cast<TreeNode>().FirstOrDefault(node => node.IsVisible);
Based on subsequent information, and if you are concerned about scrolling, the following methods will let you select the last partially or fully visible node. Using
NextVisibleNode
will not consider whether the node has scrolled out of range.
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;
}