treeview递归无限极生成树的问题

时间:2021-02-25 12:37:21
请教各位大神,这个问题思考很久,也没有想明白。需求:实现如下面所示的层级(无法上传图片,原谅我手打组织机构)
公司总部(父节点)
     领导层(子节点)
     人力资源部(子节点)
通过逐行跟踪发现,生成该组织机构树的时候,领导层下面没有其他机构的时候,结束以领导层为父节点寻找其子节点的foreach循环后,问题就来了(1)程序为什么走到81行最后一个大括号后又直接返回第78行的AddTree函数,(2)接下来还不进入AddTree函数内部,走完第79,80行大括号后,直接又从foreach中开始对将人力资源部添加到父节点公司总部的过程。
/// <summary>
        /// 将数据库中的记录添加到树中(递归)
        /// </summary> AddTree(dtDept, "a", null, tnRoot, "父机构编码", "机构编码", "机构名称", 1, srr);
        /// <param name="dtDept">部门表</param>
        /// <param name="initColumnName">节点的Code(初始化为"null")</param>
        /// <param name="tnNode">父节点的code(初始化设为null即可,因为根节点的code为null)</param>
        /// <param name="root">父节点</param>
        /// <param name="strItemPcode">父节点名(数据库中的字段名)</param>
        /// <param name="strItemcode">节点名(数据库中的字段名)</param>
        /// <param name="strItemText">显示的文本(数据库中的字段名)</param>
        /// <param name="degree">控制树展开的层数</param>
        public void AddTree(DataTable dtDept, string initColumnName, TreeNode tnNode, TreeNode root, string strItemPcode, string strItemcode, string strItemText, int degree,string rr)
        {

            DataView dvTree = new DataView(dtDept);
            dvTree.RowFilter = "[" + strItemPcode + "] = '" + initColumnName + "'";//进行行过滤
            dvTree.Sort = "报表序号";
            foreach (DataRowView Row in dvTree)
            {
                TreeNode Node = new TreeNode();
                if (tnNode == null)
                {
  
                    Node.Text = Row[strItemText].ToString();
                    Node.Tag = Row[strItemcode].ToString();
                    Node.ImageKey = Row["组织层级"].ToString();
                    Node.SelectedImageKey = Row["机关属性"].ToString();
                    Node.StateImageKey = Row["报表序号"].ToString();
                    Node.ToolTipText = Row["机构状态"].ToString();
                    if (Row["机构状态"].ToString() == "已撤销")
                    {
                        Node.ForeColor = Color.Red;
                    }
                    root.Nodes.Add(Node);
                    if (degree > 0)
                    {
                        Node.Parent.Expand();
                    }
                    AddTree(dtDept, Row[strItemcode].ToString(), Node, root, strItemPcode, strItemcode, strItemText, --degree, rr);//递归   
                }
                else
                {

                    //组织机构树中显示班长名称
                    if (Row["机关属性"].ToString() == "班组")
                    {
                        if (Row["负责人"].ToString().Length > 0)
                        {
                            Node.Text = Row[strItemText].ToString() + "(" + Row["负责人"].ToString() + ")";
                        }
                        else
                        {
                            Node.Text = Row[strItemText].ToString() + "(缺编)";
                        }
                    }
                    else
                    {
                        Node.Text = Row[strItemText].ToString();
                    }


                    //Node.Text = Row[strItemText].ToString();
                    Node.Tag = Row[strItemcode].ToString();//设主键,这是数据库中的
                    Node.ImageKey = Row["组织层级"].ToString();
                    Node.SelectedImageKey = Row["机关属性"].ToString();
                    Node.StateImageKey = Row["报表序号"].ToString();
                    Node.ToolTipText = Row["机构状态"].ToString();
                    if (Row["机构状态"].ToString() == "已撤销")
                    {
                        Node.ForeColor = Color.Red;
                    }
                    tnNode.Nodes.Add(Node);
                    if (degree > 0)
                    {
                        Node.Parent.Expand();
                    } 
                    AddTree(dtDept, Row[strItemcode].ToString(), Node, root, strItemPcode, strItemcode, strItemText, --degree, rr);
                }
            }
        }

10 个解决方案

#1


纠结某个编程语言、某段代码,是比较底层的模式,它依赖于你对数据结构、算法的知识而表现不同。

例如你这里采用了“左递归深度优先算法”来加载数据,那么在加载完第一个领导,假设它没有下属组织机构(也就是tnNode == null条件成立),那么自然程序跟踪调试指针就回到了foreach来遍历下一个领导的那条居于,而不会回到任何AddTree语句;如果是它下属有一个组织结构(人力资源部),那么加载完人力资源部之后程序跟踪调试指针自然就回到了加载这个人力资源部的AddTable语句。

也许你还是看不懂我这里的描述。我要告诉你的是,要看数据,不要死盯着代码。你每一条代码的语法都学过,但是就是不懂程序流程,这就好像是死记硬背了字典之后一个人也不能真正立刻学会写畅销小说,用再好的金笔的人也不一定能成为莫言,同样的道理。你要调试的是程序锁操作数据,不要眼里只有程序代码。那样就学“死”了。

#2


你的循环中无条件进行了递归(39、77行)
并在递归中反复对原始数据(dtDept)进行遍历

你这个递归能出的来吗?

#3


递归,相当程度上讲,它其实就是逻辑。不管是编程设计,还是设计普通的数学问题模型,会写出函数递归算法,都是最基本的能力体现。你要理解递归(或者函数映射)的知识,就要看数据产生序列。用简单的几行数学公式,可以模拟威力巨大原子弹爆炸模型,这就是它的作用。这也就是那些只会盲目抄而不会推理、归纳的人需要学习的。

#4


你应该把每一次 AddTree 方法的输入输出数据模型打印出来,然后“看数据”来调试程序流程。从vs 上的“堆栈调用”窗口进行深层调试时也是如此。

有了数据,就能调试。如果你只是说“xxxxx语句执行完之后为什么走到xxxxxxx,而有时又走到另一个地方xxxxxxx”,这就很低效。你应该只从数据访问序列上来理解程序行为,假设这根本不是c#写的程序,而是F#写的程序,或者是 List 写的程序,你也应该能够抛开编程语言而发现主要的编程设计问题。

#5


或者是 List 写的程序   -->   或者是 Lisp 写的程序

当你的实际代码执行时的数据序列,跟需求不一致的时候,这时候应该“删减代码”,把可以抽取出去单独成为一个独立方法的代码都删减掉。例如你的 AddTree 方法里边应该只剩下最多7行代码,而不是70行代码。

代码可以随便写,只要看数据产生序列是否正确就行了。代码减少到7行,结合数据产生次序,那么一下子就看出问题来了。

#6


你这是从一个DataTable作为数据源,查找符合某一级条件的集合。比如根集合:公司总部,key=-1

数据看来是是这样的

1 -1  公司总部1

2 1  领导层1
3 1  领导层1

4 2 人力资源部1
5 2 人力资源部2

那在查找 【人力资源部2】的子节点时,发现没有  [x 5 某某单位]的数据,那就是叶子节点了,直接return了呗

#7


递归在调试时,会由于重复调用某个方法,而看起来断点很混乱。
其实说白了,就是第一次进入递归,调用方法A,还没结束,但是第二次递归已经来了,又调用了方法A,所以断点会跳来跳去。
可是你心里应该清楚,这是递归的第二次结果进来了。 treeview递归无限极生成树的问题

#8


感谢您详细的回答,您很专业,一语中的的看出了我的问题。我的编程基础比较薄弱,感谢您耐心指点。
关于我提的第二个问题,程序走到81行最后一个大括号后又直接返回第78行的AddTree函数,那么按照常规理解,此时就需要进入AddTree的函数体内部(也就是第15行),可是程序此时为什么又直接走向78行的大括号?

#9


引用 楼主 mvpying 的回复:
请教各位大神,这个问题思考很久,也没有想明白。需求:实现如下面所示的层级(无法上传图片,原谅我手打组织机构)
公司总部(父节点)
     领导层(子节点)
     人力资源部(子节点)
通过逐行跟踪发现,生成该组织机构树的时候,领导层下面没有其他机构的时候,结束以领导层为父节点寻找其子节点的foreach循环后,问题就来了(1)程序为什么走到81行最后一个大括号后又直接返回第78行的AddTree函数,(2)接下来还不进入AddTree函数内部,走完第79,80行大括号后,直接又从foreach中开始对将人力资源部添加到父节点公司总部的过程。
/// <summary>
        /// 将数据库中的记录添加到树中(递归)
        /// </summary> AddTree(dtDept, "a", null, tnRoot, "父机构编码", "机构编码", "机构名称", 1, srr);
        /// <param name="dtDept">部门表</param>
        /// <param name="initColumnName">节点的Code(初始化为"null")</param>
        /// <param name="tnNode">父节点的code(初始化设为null即可,因为根节点的code为null)</param>
        /// <param name="root">父节点</param>
        /// <param name="strItemPcode">父节点名(数据库中的字段名)</param>
        /// <param name="strItemcode">节点名(数据库中的字段名)</param>
        /// <param name="strItemText">显示的文本(数据库中的字段名)</param>
        /// <param name="degree">控制树展开的层数</param>
        public void AddTree(DataTable dtDept, string initColumnName, TreeNode tnNode, TreeNode root, string strItemPcode, string strItemcode, string strItemText, int degree,string rr)
        {

            DataView dvTree = new DataView(dtDept);
            dvTree.RowFilter = "[" + strItemPcode + "] = '" + initColumnName + "'";//进行行过滤
            dvTree.Sort = "报表序号";
            foreach (DataRowView Row in dvTree)
            {
                TreeNode Node = new TreeNode();
                if (tnNode == null)
                {
  
                    Node.Text = Row[strItemText].ToString();
                    Node.Tag = Row[strItemcode].ToString();
                    Node.ImageKey = Row["组织层级"].ToString();
                    Node.SelectedImageKey = Row["机关属性"].ToString();
                    Node.StateImageKey = Row["报表序号"].ToString();
                    Node.ToolTipText = Row["机构状态"].ToString();
                    if (Row["机构状态"].ToString() == "已撤销")
                    {
                        Node.ForeColor = Color.Red;
                    }
                    root.Nodes.Add(Node);
                    if (degree > 0)
                    {
                        Node.Parent.Expand();
                    }
                    AddTree(dtDept, Row[strItemcode].ToString(), Node, root, strItemPcode, strItemcode, strItemText, --degree, rr);//递归   
                }
                else
                {

                    //组织机构树中显示班长名称
                    if (Row["机关属性"].ToString() == "班组")
                    {
                        if (Row["负责人"].ToString().Length > 0)
                        {
                            Node.Text = Row[strItemText].ToString() + "(" + Row["负责人"].ToString() + ")";
                        }
                        else
                        {
                            Node.Text = Row[strItemText].ToString() + "(缺编)";
                        }
                    }
                    else
                    {
                        Node.Text = Row[strItemText].ToString();
                    }


                    //Node.Text = Row[strItemText].ToString();
                    Node.Tag = Row[strItemcode].ToString();//设主键,这是数据库中的
                    Node.ImageKey = Row["组织层级"].ToString();
                    Node.SelectedImageKey = Row["机关属性"].ToString();
                    Node.StateImageKey = Row["报表序号"].ToString();
                    Node.ToolTipText = Row["机构状态"].ToString();
                    if (Row["机构状态"].ToString() == "已撤销")
                    {
                        Node.ForeColor = Color.Red;
                    }
                    tnNode.Nodes.Add(Node);
                    if (degree > 0)
                    {
                        Node.Parent.Expand();
                    } 
                    AddTree(dtDept, Row[strItemcode].ToString(), Node, root, strItemPcode, strItemcode, strItemText, --degree, rr);
                }
            }
        }

感谢您详细的回答,您很专业,一语中的的看出了我的问题。我的编程基础比较薄弱,感谢您耐心指点。
关于我提的第二个问题,程序走到81行最后一个大括号后又直接返回第78行的AddTree函数,那么按照常规理解,此时就需要进入AddTree的函数体内部(也就是第15行),可是程序此时为什么又直接走向78行的大括号?

#10


引用 1 楼 sp1234 的回复:
纠结某个编程语言、某段代码,是比较底层的模式,它依赖于你对数据结构、算法的知识而表现不同。

例如你这里采用了“左递归深度优先算法”来加载数据,那么在加载完第一个领导,假设它没有下属组织机构(也就是tnNode == null条件成立),那么自然程序跟踪调试指针就回到了foreach来遍历下一个领导的那条居于,而不会回到任何AddTree语句;如果是它下属有一个组织结构(人力资源部),那么加载完人力资源部之后程序跟踪调试指针自然就回到了加载这个人力资源部的AddTable语句。

也许你还是看不懂我这里的描述。我要告诉你的是,要看数据,不要死盯着代码。你每一条代码的语法都学过,但是就是不懂程序流程,这就好像是死记硬背了字典之后一个人也不能真正立刻学会写畅销小说,用再好的金笔的人也不一定能成为莫言,同样的道理。你要调试的是程序锁操作数据,不要眼里只有程序代码。那样就学“死”了。

感谢您详细的回答,您很专业,一语中的的看出了我的问题。我的编程基础比较薄弱,感谢您耐心指点。
关于我提的第二个问题,程序走到81行最后一个大括号后又直接返回第78行的AddTree函数,那么按照常规理解,此时就需要进入AddTree的函数体内部(也就是第15行),可是程序此时为什么又直接走向78行的大括号?

#1


纠结某个编程语言、某段代码,是比较底层的模式,它依赖于你对数据结构、算法的知识而表现不同。

例如你这里采用了“左递归深度优先算法”来加载数据,那么在加载完第一个领导,假设它没有下属组织机构(也就是tnNode == null条件成立),那么自然程序跟踪调试指针就回到了foreach来遍历下一个领导的那条居于,而不会回到任何AddTree语句;如果是它下属有一个组织结构(人力资源部),那么加载完人力资源部之后程序跟踪调试指针自然就回到了加载这个人力资源部的AddTable语句。

也许你还是看不懂我这里的描述。我要告诉你的是,要看数据,不要死盯着代码。你每一条代码的语法都学过,但是就是不懂程序流程,这就好像是死记硬背了字典之后一个人也不能真正立刻学会写畅销小说,用再好的金笔的人也不一定能成为莫言,同样的道理。你要调试的是程序锁操作数据,不要眼里只有程序代码。那样就学“死”了。

#2


你的循环中无条件进行了递归(39、77行)
并在递归中反复对原始数据(dtDept)进行遍历

你这个递归能出的来吗?

#3


递归,相当程度上讲,它其实就是逻辑。不管是编程设计,还是设计普通的数学问题模型,会写出函数递归算法,都是最基本的能力体现。你要理解递归(或者函数映射)的知识,就要看数据产生序列。用简单的几行数学公式,可以模拟威力巨大原子弹爆炸模型,这就是它的作用。这也就是那些只会盲目抄而不会推理、归纳的人需要学习的。

#4


你应该把每一次 AddTree 方法的输入输出数据模型打印出来,然后“看数据”来调试程序流程。从vs 上的“堆栈调用”窗口进行深层调试时也是如此。

有了数据,就能调试。如果你只是说“xxxxx语句执行完之后为什么走到xxxxxxx,而有时又走到另一个地方xxxxxxx”,这就很低效。你应该只从数据访问序列上来理解程序行为,假设这根本不是c#写的程序,而是F#写的程序,或者是 List 写的程序,你也应该能够抛开编程语言而发现主要的编程设计问题。

#5


或者是 List 写的程序   -->   或者是 Lisp 写的程序

当你的实际代码执行时的数据序列,跟需求不一致的时候,这时候应该“删减代码”,把可以抽取出去单独成为一个独立方法的代码都删减掉。例如你的 AddTree 方法里边应该只剩下最多7行代码,而不是70行代码。

代码可以随便写,只要看数据产生序列是否正确就行了。代码减少到7行,结合数据产生次序,那么一下子就看出问题来了。

#6


你这是从一个DataTable作为数据源,查找符合某一级条件的集合。比如根集合:公司总部,key=-1

数据看来是是这样的

1 -1  公司总部1

2 1  领导层1
3 1  领导层1

4 2 人力资源部1
5 2 人力资源部2

那在查找 【人力资源部2】的子节点时,发现没有  [x 5 某某单位]的数据,那就是叶子节点了,直接return了呗

#7


递归在调试时,会由于重复调用某个方法,而看起来断点很混乱。
其实说白了,就是第一次进入递归,调用方法A,还没结束,但是第二次递归已经来了,又调用了方法A,所以断点会跳来跳去。
可是你心里应该清楚,这是递归的第二次结果进来了。 treeview递归无限极生成树的问题

#8


感谢您详细的回答,您很专业,一语中的的看出了我的问题。我的编程基础比较薄弱,感谢您耐心指点。
关于我提的第二个问题,程序走到81行最后一个大括号后又直接返回第78行的AddTree函数,那么按照常规理解,此时就需要进入AddTree的函数体内部(也就是第15行),可是程序此时为什么又直接走向78行的大括号?

#9


引用 楼主 mvpying 的回复:
请教各位大神,这个问题思考很久,也没有想明白。需求:实现如下面所示的层级(无法上传图片,原谅我手打组织机构)
公司总部(父节点)
     领导层(子节点)
     人力资源部(子节点)
通过逐行跟踪发现,生成该组织机构树的时候,领导层下面没有其他机构的时候,结束以领导层为父节点寻找其子节点的foreach循环后,问题就来了(1)程序为什么走到81行最后一个大括号后又直接返回第78行的AddTree函数,(2)接下来还不进入AddTree函数内部,走完第79,80行大括号后,直接又从foreach中开始对将人力资源部添加到父节点公司总部的过程。
/// <summary>
        /// 将数据库中的记录添加到树中(递归)
        /// </summary> AddTree(dtDept, "a", null, tnRoot, "父机构编码", "机构编码", "机构名称", 1, srr);
        /// <param name="dtDept">部门表</param>
        /// <param name="initColumnName">节点的Code(初始化为"null")</param>
        /// <param name="tnNode">父节点的code(初始化设为null即可,因为根节点的code为null)</param>
        /// <param name="root">父节点</param>
        /// <param name="strItemPcode">父节点名(数据库中的字段名)</param>
        /// <param name="strItemcode">节点名(数据库中的字段名)</param>
        /// <param name="strItemText">显示的文本(数据库中的字段名)</param>
        /// <param name="degree">控制树展开的层数</param>
        public void AddTree(DataTable dtDept, string initColumnName, TreeNode tnNode, TreeNode root, string strItemPcode, string strItemcode, string strItemText, int degree,string rr)
        {

            DataView dvTree = new DataView(dtDept);
            dvTree.RowFilter = "[" + strItemPcode + "] = '" + initColumnName + "'";//进行行过滤
            dvTree.Sort = "报表序号";
            foreach (DataRowView Row in dvTree)
            {
                TreeNode Node = new TreeNode();
                if (tnNode == null)
                {
  
                    Node.Text = Row[strItemText].ToString();
                    Node.Tag = Row[strItemcode].ToString();
                    Node.ImageKey = Row["组织层级"].ToString();
                    Node.SelectedImageKey = Row["机关属性"].ToString();
                    Node.StateImageKey = Row["报表序号"].ToString();
                    Node.ToolTipText = Row["机构状态"].ToString();
                    if (Row["机构状态"].ToString() == "已撤销")
                    {
                        Node.ForeColor = Color.Red;
                    }
                    root.Nodes.Add(Node);
                    if (degree > 0)
                    {
                        Node.Parent.Expand();
                    }
                    AddTree(dtDept, Row[strItemcode].ToString(), Node, root, strItemPcode, strItemcode, strItemText, --degree, rr);//递归   
                }
                else
                {

                    //组织机构树中显示班长名称
                    if (Row["机关属性"].ToString() == "班组")
                    {
                        if (Row["负责人"].ToString().Length > 0)
                        {
                            Node.Text = Row[strItemText].ToString() + "(" + Row["负责人"].ToString() + ")";
                        }
                        else
                        {
                            Node.Text = Row[strItemText].ToString() + "(缺编)";
                        }
                    }
                    else
                    {
                        Node.Text = Row[strItemText].ToString();
                    }


                    //Node.Text = Row[strItemText].ToString();
                    Node.Tag = Row[strItemcode].ToString();//设主键,这是数据库中的
                    Node.ImageKey = Row["组织层级"].ToString();
                    Node.SelectedImageKey = Row["机关属性"].ToString();
                    Node.StateImageKey = Row["报表序号"].ToString();
                    Node.ToolTipText = Row["机构状态"].ToString();
                    if (Row["机构状态"].ToString() == "已撤销")
                    {
                        Node.ForeColor = Color.Red;
                    }
                    tnNode.Nodes.Add(Node);
                    if (degree > 0)
                    {
                        Node.Parent.Expand();
                    } 
                    AddTree(dtDept, Row[strItemcode].ToString(), Node, root, strItemPcode, strItemcode, strItemText, --degree, rr);
                }
            }
        }

感谢您详细的回答,您很专业,一语中的的看出了我的问题。我的编程基础比较薄弱,感谢您耐心指点。
关于我提的第二个问题,程序走到81行最后一个大括号后又直接返回第78行的AddTree函数,那么按照常规理解,此时就需要进入AddTree的函数体内部(也就是第15行),可是程序此时为什么又直接走向78行的大括号?

#10


引用 1 楼 sp1234 的回复:
纠结某个编程语言、某段代码,是比较底层的模式,它依赖于你对数据结构、算法的知识而表现不同。

例如你这里采用了“左递归深度优先算法”来加载数据,那么在加载完第一个领导,假设它没有下属组织机构(也就是tnNode == null条件成立),那么自然程序跟踪调试指针就回到了foreach来遍历下一个领导的那条居于,而不会回到任何AddTree语句;如果是它下属有一个组织结构(人力资源部),那么加载完人力资源部之后程序跟踪调试指针自然就回到了加载这个人力资源部的AddTable语句。

也许你还是看不懂我这里的描述。我要告诉你的是,要看数据,不要死盯着代码。你每一条代码的语法都学过,但是就是不懂程序流程,这就好像是死记硬背了字典之后一个人也不能真正立刻学会写畅销小说,用再好的金笔的人也不一定能成为莫言,同样的道理。你要调试的是程序锁操作数据,不要眼里只有程序代码。那样就学“死”了。

感谢您详细的回答,您很专业,一语中的的看出了我的问题。我的编程基础比较薄弱,感谢您耐心指点。
关于我提的第二个问题,程序走到81行最后一个大括号后又直接返回第78行的AddTree函数,那么按照常规理解,此时就需要进入AddTree的函数体内部(也就是第15行),可是程序此时为什么又直接走向78行的大括号?