vb.net LINQ 在树视图中获取最深层次的节点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14141792/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 11:45:16  来源:igfitidea点击:

LINQ to get deepest level nodes in a treeview

c#vb.netlinq

提问by John Bustos

Suppose I have a WinForms Treeview that looks as follows:

假设我有一个如下所示的 WinForms Treeview:

Parent1
   Child1
      Sub-Child1
         DeepestNode1
         DeepestNode2
         DeepestNode3
      Sub-Child2
         DeepestNode4
         DeepestNode5
         DeepestNode6
   Child2
      Sub-Child3
      Sub-Child4
      Sub-Child5
      Sub-Child6
   Child3
      (no children)


I would like to create a function along the lines of:

我想按照以下方式创建一个函数:

Function GetDeepestChildren(MyNode as Treenode) as List(Of Treenode)

Where, if the results would look like:

如果结果如下所示:

GetDeepestChildren(Parent1) = {DeepestNode1, DeepestNode2, DeepestNode3, DeepestNode4, DeepestNode5, DeepestNode6}

GetDeepestChildren(Sub-Child1) = {DeepestNode1, DeepestNode2, DeepestNode3}

GetDeepestChildren(Child2) = {Sub-Child3, Sub-Child4, Sub-Child5, Sub-Child6}

GetDeepestChildren(Child3) = Empty list

... In other words, always go to the deepest level you can from the node given and return the children - Even if they're split between different parents (as was the case in Parent1).

... 换句话说,总是从给定的节点进入最深的层次并返回子节点 - 即使它们在不同的父节点之间分裂(如 中的情况Parent1)。

I have created a function that will tell me how many levels deeper a node goes that looks like:

我创建了一个函数,它会告诉我一个节点的深度有多少层,如下所示:

    Public Function GetDeepestChildNodeLevel(ByVal ParentNode As TreeNode) As Integer
        Dim subLevel = ParentNode.Nodes.Cast(Of TreeNode).Select(Function(subNode) GetDeepestChildNodeLevel(subNode))
        Return If(subLevel.Count = 0, 0, subLevel.Max() + 1)
    End Function

So I know from what level to get the children, what i'm looking for is a function that can do this - Somethign along the lines of:

所以我知道从什么级别得到孩子,我正在寻找的是一个可以做到这一点的功能 - 一些类似的东西:

Function GetDeepestChildren(MyNode as Treenode) as List(Of Treenode)
       Return All child nodes where level = GetDeepestChildNodeLevel(MyNode)
End function

I hope this makes sense - Thanks!

我希望这是有道理的 - 谢谢!

采纳答案by dasblinkenlight

In C# you can do it with yield returnor with a recursive lambda. Here is an example of the second approach:

在 C# 中,您可以使用yield return或使用递归 lambda 来完成。以下是第二种方法的示例:

Func<TreeNode,IEnumerable<TreeNode>> getChildren = null;
getChildren = n => {
    if (n.Nodes.Count != 0) {
        var list = new List<TreeNode>(n.Nodes.Where(c => c.Nodes.Count == 0));
        foreach (var c in n.Nodes) {
            // Note the recursive call below:
            list.AddRange(getChildren(c));
        }
        return list;
    } else {
        return new TreeNode[0];
    }
};
var res = getChildren(myTree);

回答by Hogan

Here is a version using XML -- the translation should be easy. I used linqPadwhich I recommend for this kind of stuff, you can run this and see it work directly in linkPad

这是一个使用 XML 的版本——翻译应该很容易。我使用了linqPad,我为这类东西推荐了它,你可以运行它并直接在 linkPad 中看到它的工作

WalkDeep(tree,getDeep(tree)) returns:

<DeepestNode1 /> 
<DeepestNode2 /> 
<DeepestNode3 /> 
<DeepestNode4 /> 
<DeepestNode5 /> 
<DeepestNode6 /> 

The C# code is nicer because you can use yield

C# 代码更好,因为您可以使用 yield

VB Code

VB代码

function getDeep( e as XElement) as integer
  if (e.HasElements)
    return 1 + e.Elements().Select(Function(c) getDeep(c)).Max()
  else
    return 1
  end if  
end function

function WalkDeep(root as XElement,find as integer,optional mylevel as integer = 1) as IEnumerable(of XElement)
  Dim result As New List(Of XElement)

  if find = mylevel 
    result.Add(root)
  else 
    if root.HasElements
      for each c as XElement in root.Elements()
        for each r as XElement in WalkDeep(c,find,mylevel+1)
            result.Add(r)
        next
      next  
    end if
  end if

  return result
end function

Sub Main
  dim tree as XElement = <Parent1>
     <Child1>
        <Sub-Child1>
           <DeepestNode1/>
           <DeepestNode2/>
           <DeepestNode3/>
        </Sub-Child1>   
        <Sub-Child2>
           <DeepestNode4/>
           <DeepestNode5/>
           <DeepestNode6/>
        </Sub-Child2>   
     </Child1>      
     <Child2>
        <Sub-Child3/>
        <Sub-Child4/>
        <Sub-Child5/>
        <Sub-Child6/>
     </Child2>   
     <Child3 />
  </Parent1>   

  WalkDeep(tree,getDeep(tree)).Select(function(x) x.Name.LocalName).Dump()
End Sub

C# Code:

C# 代码:

int getDeep(XElement e)
{
  if (e.HasElements)
    return 1 + e.Elements().Select(c => getDeep(c)).Max();
  else
    return 1;
}

IEnumerable<XElement> WalkDeep(XElement root,int find, int mylevel=1)
{   
  if (find == mylevel) yield return root;

  if (root.HasElements)
  {
    foreach(XElement c in root.Elements())
    {
      foreach(XElement r in WalkDeep(c,find,mylevel+1))
        yield return r;

    }
  }

  yield break;
}

void Main()
{
  XElement tree = XElement.Parse (@"
  <Parent1>
     <Child1>
        <Sub-Child1>
           <DeepestNode1/>
           <DeepestNode2/>
           <DeepestNode3/>
        </Sub-Child1>   
        <Sub-Child2>
           <DeepestNode4/>
           <DeepestNode5/>
           <DeepestNode6/>
        </Sub-Child2>   
     </Child1>      
     <Child2>
        <Sub-Child3/>
        <Sub-Child4/>
        <Sub-Child5/>
        <Sub-Child6/>
     </Child2>   
     <Child3 />
  </Parent1>   
  ");

  WalkDeep(tree,getDeep(tree)).Dump();
} 

回答by John Bustos

This is a VB.Net re-make I created of @dasblinkenlight's solution - It worked perfectly and I'm just putting it here in case anyone in the future needs the solution in VB.

这是我用@dasblinkenlight 的解决方案创建的 VB.Net 重新制作 - 它运行良好,我只是把它放在这里,以防将来有人需要 VB 中的解决方案。

    Public Function GetDeepestChildNodes(ByVal ParentNode As TreeNode) As List(Of TreeNode)
        Dim RetVal As New List(Of TreeNode)

        If ParentNode.Nodes.Count > 0 Then
            RetVal = (From nd As TreeNode In ParentNode.Nodes
                   Where nd.Nodes.Count = 0
                   Select nd).ToList

            For Each nd In ParentNode.Nodes
                RetVal.AddRange(GetDeepestChildNodes(nd))
            Next
        End If

        Return RetVal
    End Function

Thank you all again for your help!!!

再次感谢大家的帮助!!!

回答by Fredou

it is not linq, since i think in this case it should not be done by linq sorry if it's not what you asked but this work. it is not fullproof but at least you wont get stackoverflow if you get a crazy tree

它不是 linq,因为我认为在这种情况下它不应该由 linq 完成,如果不是你问的而是这项工作,抱歉。它不是完全可靠的,但至少如果你得到一棵疯狂的树,你不会得到 stackoverflow

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Dim test1 = GetDeepestChildren(TreeView1.Nodes(0))
    Dim test2 = GetDeepestChildren(TreeView1.Nodes(0).Nodes(0).Nodes(0))
    Dim test3 = GetDeepestChildren(TreeView1.Nodes(0).Nodes(1))
    Dim test4 = GetDeepestChildren(TreeView1.Nodes(0).Nodes(2))
End Sub

Private Function GetDeepestChildren(ByVal node As TreeNode) As List(Of TreeNode)
    Dim deepestList As New List(Of TreeNode)

    If node.Nodes.Count = 0 Then
        Return deepestList
    End If

    Dim nodes As New Stack(Of TreeNode)
    For Each n As TreeNode In node.Nodes
        nodes.Push(n)
    Next

    Dim deepest As Integer = 0
    Do Until nodes.Count = 0
        node = nodes.Pop
        If node.Nodes.Count = 0 Then
            If deepest < node.Level Then
                deepest = node.Level
                deepestList.Clear()
                deepestList.Add(node)
            ElseIf deepest = node.Level Then
                deepestList.Add(node)
            End If
        Else
            For Each n As TreeNode In node.Nodes
                nodes.Push(n)
            Next
        End If
    Loop

    Return deepestList
End Function

回答by gfyans

I'm not familiar with the TreeView control, but is the Level property of the TreeNode any good to you?

我不熟悉 TreeView 控件,但是 TreeNode 的 Level 属性对您有好处吗?

http://msdn.microsoft.com/en-us/library/system.windows.forms.treenode.level.aspx

http://msdn.microsoft.com/en-us/library/system.windows.forms.treenode.level.aspx

If you know the deepest level, you could do this:

如果你知道最深层次,你可以这样做:

C#

C#

private List<TreeNode> GetDeepestChildren(int level)
{
    return (from p in treeView1.Nodes.Cast<TreeNode>() where p.Level == level select p).ToList();
}

VB

VB

Private Function GetDeepestChildren(level As Integer) As List(Of TreeNode)
    Return (From p In treeView1.Nodes.Cast(Of TreeNode)() Where p.Level = levelp).ToList()
End Function

Greg.

格雷格。

回答by JerKimball

This is justa slight modification of @dasblinkenlight 's answer, so don't upvote this!

只是对@dasblinkenlight 的回答的轻微修改,所以不要点赞!

Just a matter of personal style, but I rather like the recursive call as such:

只是个人风格的问题,但我更喜欢这样的递归调用:

IEnumerable<TreeNode> WalkNodes(TreeNode root)
{   
    yield return root;
    var children = root.Nodes.Cast<TreeNode>();
    foreach (var child in children)
    {
        foreach(var subChild in WalkNodes(child))
        {
            yield return subChild;
        }
    }
}

And called via:

并通过以下方式调用:

foreach (var node in treeView.Nodes.Cast<TreeNode>())
{
    var walkedFrom = WalkNodes(node);
    foreach (var subNode in walkedFrom)
    {
        Console.WriteLine(subNode.Text);
    }
}

回答by Stokedout

Tried a recursive function yet? Not sure in VB.Net but C# would look like

试过递归函数了吗?在 VB.Net 中不确定,但 C# 看起来像

public List<TreeNode> GetDeepestChildren(Treenode MyNode)
{
   if (MyNode.Children.Count > 0)
        GetDeepestChildren(Treenode MyNode);
   else
        return MyNode.Children;
}

This hasnt been compiled or tested but the idea is there and should return the deepest children for any given node.

这还没有被编译或测试,但这个想法已经存在并且应该返回任何给定节点的最深子节点。