树型BOM结构递归问题(兼C#)

时间:2022-03-14 11:57:08

另外我将数据库中的表结构贴出来如下:
表名:ICBOMGROUP

FInterID(BOM内码)    FParentID(父BOM内码)  FName(名称)       FNumber(产品代码)
1039                               0                            产品                        0
2296                               0                            喷印机壳                  1
6497                               0                            丝印彩盒                  2
7035                               0                            外发加工                  3
1041                               1039                       VGA电视盒              01
1071                               1041                       TV3488B系列          01.01
1140                               1071                       TV2188EBOM          01.01.01
1144                               1140                       普通订单                 01.01.01.01 
1146                               1144                       TV2188E 中文BOM   01.01.01.01.01
1145                               1140                       特殊订单                 01.01.01.02
1129                               1039                       XGA电视盒              02
1199                               1129                       TV5811T系列           02.02
1212                               1199                       TV5811BOM            02.02.01
2297                               2296                       机壳-上盖                1.01
2308                               2297                       GM998                    1.01.01
2312                               2308                       GM998上盖 灰色      1.01.01.01
7039                               7035                       支架组件                  3.01
7040                               7039                       PD1048                   3.01.01
7172                               7040                       PD1048支架组件       3.01.01.01

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace WindowsFormsApplication17
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            TreeView tv = new TreeView();
            tv.AfterSelect += new TreeViewEventHandler(tv_AfterSelect);
            tv.Dock = DockStyle.Fill;
            this.BuildTree(tv.Nodes, 0);
            this.Controls.Add(tv);
        }

        private void tv_AfterSelect(object sender, TreeViewEventArgs e)
        {
            // 选择节点后,获取当前节点的信息
            // 你可以将下列信息赋给你对应的控件
            DataRow row = (DataRow)e.Node.Tag;

            Int32 FInterID = (Int32)row["FInterID"];
            Int32 ParentId = (Int32)row["FParentID"];
            String FName = (String)row["FName"];
        }

        private DataTable fSampleDataSource;
        public DataTable SampleDataSource
        {
            get  //这一段是我连接本人数据的代码
            {
                SqlConnection thisconnection = new SqlConnection("server=(local);database=GADMEIERP;Integrated Security=true;");
                SqlCommand cmd = thisconnection.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select FInterID,FParentID,FName,FNumber from ICBOMGROUP";
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = cmd;
                DataSet ds = new DataSet();
                adapter.Fill(ds, "ICBOMGROUP");
                this.fSampleDataSource = ds.Tables[0];
                return fSampleDataSource;
            }
        }

        /// <summary>
        /// 创建目录和文件树
        /// </summary>
        /// <param name="fNodes"></param>
        /// <param name="fPath"></param>
        public void BuildTree(TreeNodeCollection fNodes, Int32 fParentId)
        {
            foreach (DataRow row
                in this.SampleDataSource.Select(String.Format("FParentID = {0}", fParentId)))
            {
                TreeNode node = new TreeNode();
                node.Text = row["FName"].ToString();
                node.Tag = row;
                // 递归创建子节点
                this.BuildTree(node.Nodes, (Int32)row["FInterID"]);
                fNodes.Add(node);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }
}




在C#FORM界面中,要求有一个treeview控件,根据表结构,生成根结点和子结点:
比如说FParentID为0,为一阶,再根据父节点和子节点依次生成下面的结点,例:
0(产品)
   01(VGA电视盒)
     01.01(TV3488B系列)
        01.01.01(TV2188E BOM)
            01.01.01.01(普通订单)
            01.01.01.02(特殊订单)
省略了

请高手指点下....连接datagridview控件的语句,偶学习...因为没看过高级编程,摸着石头过河,快淹死偶了...

20 个解决方案

#1


题目:树型BOM结构递归问题
问题:连接datagridview控件的语句?

#2


先只求根结点,加到树上,然后在单击的时候,展开下层,这样能提高效率,防止一次性加载时间过长,另外代码也好写。在点击树结点时,根据树结点属性,如ID得到其所有子结点的数据,并将其加载到表格中。

#3


先获取FParentID(父BOM内码) = 0的做为根节点.

然后使用循环逐一获取某根节点的各级节点.

C#不会,帮顶.

#5


如果是SQL,参考如下:


/*
标题:查询所有*节点及其子节点的例
地址:http://topic.csdn.net/u/20090323/21/63a91f51-c4df-464d-ba18-64343deb4e3a.html
作者:爱新觉罗·毓华(十八年风雨,守得冰山雪莲花开)
时间:2009-03-23
地点:广东深圳
*/

[code=SQL]create table Area (id int identity,Name varchar(10) ,order_by int ,father_ID int )
insert into area values('广东省',2,0) 
insert into area values('四川省',2,0) 
insert into area values('湖北省',2,0) 
insert into area values('东莞市',1,1) 
insert into area values('广州市',1,1) 
insert into area values('天河区',0,5) 
insert into area values('绵阳市',1,2) 
insert into area values('武汉市',1,3) 
insert into area values('汉口区',0,8) 
insert into area values('随州市',1,3)
go

select * from area

drop table area

/*
id          Name       order_by    father_ID   
----------- ---------- ----------- ----------- 
1           广东省        2           0
2           四川省        2           0
3           湖北省        2           0
4           东莞市        1           1
5           广州市        1           1
6           天河区        0           5
7           绵阳市        1           2
8           武汉市        1           3
9           汉口区        0           8
10          随州市        1           3

(所影响的行数为 10 行)

要求显示为:
name           
-------------- 
广东省
  东莞市
  广州市
    天河区
四川省
  绵阳市
湖北省
  武汉市
    汉口区
  随州市

(所影响的行数为 10 行)
*/



--创建原始表
create table Area (id int identity,Name varchar(10) ,order_by int ,father_ID int )
insert into area values('广东省',2,0) 
insert into area values('四川省',2,0) 
insert into area values('湖北省',2,0) 
insert into area values('东莞市',1,1) 
insert into area values('广州市',1,1) 
insert into area values('天河区',0,5) 
insert into area values('绵阳市',1,2) 
insert into area values('武汉市',1,3) 
insert into area values('汉口区',0,8) 
insert into area values('随州市',1,3)
--创建临时表
create table tmp (id int identity,Name varchar(10) ,order_by int ,father_ID int )
go

--创建查询指定节点及其所有子节点的函数
create function f_cid(@ID int) returns @t_level table(id int , level int)
as
begin
  declare @level int
  set @level = 1
  insert into @t_level select @id , @level
  while @@ROWCOUNT > 0
  begin
    set @level = @level + 1
    insert into @t_level select a.id , @level
    from area a , @t_Level b
    where a.father_ID = b.id and b.level = @level - 1
  end
  return
end
go

--创建存储过程并将数据插入临时表
create proc my_proc 
as
begin
  declare @id as int
  set @id = 0
  while exists(select 1 from area where order_by = 2 and id > @id)
  begin
    set @id = (select min(id) from area where order_by = 2 and id > @id)
    insert into tmp(Name ,order_by ,father_ID) select a.name,a.order_by ,a.father_id from area a , f_cid(@id) b where a.id = b.id order by a.id 
  end
end
go
exec my_proc

--从临时表提取数据并显示
select case when order_by = 2 then name
            when order_by = 1 then '  ' + name
            when order_by = 0 then '    ' + name
       end name
from tmp order by id

drop function f_cid
drop proc my_proc
drop table area , tmp

/*
name           
-------------- 
广东省
  东莞市
  广州市
    天河区
四川省
  绵阳市
湖北省
  武汉市
    汉口区
  随州市

(所影响的行数为 10 行)

*/

#6


谢谢各位了,哎..代码编译通过,就是生成不了数据,郁闷啊...CSDN.C#论坛没几个高手解答

#7


该回复于2009-09-25 10:48:41被版主删除

#8


帮顶

#9


BOM展现为什么使用Gridview,而不使用树形结构。比如说树形菜单。

#10


展阶后要有相关的BOM信息哈,要求显示的信息不同

#11


C#树形结构?

#12


感觉做法没有问题啊,应该是根节点
或者父子节点设定的问题,递归的时候这个挺难调试的
你先不要忙着绑定treeview那边,先递归显示到一个textbox里整理好递归那个函数再绑定treeview了
其实用devexpress有专门的控件

#13


是的。C#树型结构.代码没有任何问题,就是ICBOMGROUP这张表里数据量庞大,调试的时候需要非常长的时间,,寻求简便方法

#14


给个TREEVIEW的例子吧

#15


上面就是在FORM窗体中添加TREEVIEW啊..自动递归添加树节点和根节点的.只是没做到单击一个结点,只是调用这个结点的下一个子结点,而是将所有结点都计算了一次,所以导致数据量庞大...寻求简便方法....

#16


c#不懂,只能帮顶了!

#17


C#,你进的版块不对,这边只针对数据库.

#18


等了几天还是没高手来解答

#19


treeview递归网上有很多C#的代码例

点到treeview结点显示BOM当前层次只用一个datagridview吗?

父件是单一记录,对应子件可能是多条记录,主从表结构
用两个datagridview 或者 多个LABEL 和 一个datagridview吧。

#20


在buildtree过程中,只查询FParentID=0的记录。

public void BuildTree(TreeNodeCollection fNodes, Int32 fParentId)
        {
            SqlConnection thisconnection = new SqlConnection("server=(local);database=GADMEIERP;Integrated Security=true;");
                SqlCommand cmd = thisconnection.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select FInterID,FParentID,FName,FNumber from ICBOMGROUP where
                        FParentID=0 ";
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = cmd;
                DataSet ds = new DataSet();
                adapter.Fill(ds, "ICBOMGROUP");
                this.fSampleDataSource = ds.Tables[0]; 
            //以下填充树
            foreach (DataRow row
                in this.SampleDataSource.Select(String.Format("FParentID = {0}", fParentId)))
            {
                TreeNode node = new TreeNode();
                node.Text = row["FName"].ToString();
                node.Tag = row["FInterID"].ToString();

                tv.Nodes.Add(node);
            }
        } 

加载了所有根结点后,在点击树结点时,查询相应的子结点,并加入树中。

private void tv_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if e.node.nodes.count>0 then //如果有子结点,则不需要查询其子结点
              return;
            string InterID= (string)e.Node.Tag;
             SqlConnection thisconnection = new SqlConnection("server=(local);database=GADMEIERP;Integrated Security=true;");
                SqlCommand cmd = thisconnection.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select FInterID,FParentID,FName,FNumber from ICBOMGROUP where
                        FParentID='"+InterID+"'";
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = cmd;
                DataSet ds = new DataSet();
                adapter.Fill(ds, "ICBOMGROUP");
                this.fSampleDataSource = ds.Tables[0]; 
            //以下填充树
            foreach (DataRow row
                in this.SampleDataSource.Select(String.Format("FParentID = {0}", fParentId)))
            {
                TreeNode node = new TreeNode();
                node.Text = row["FName"].ToString();
                node.Tag = row["FInterID"].ToString();

                //应该是类似的方法,因为手头没有现成的例子,LZ凑合着看吧。
                e.Node.AddChildNode(node);
                //或e.Node.Nodes.add(node);
            }
           
        } 


代码不一定准确,但基本上这个思路。LZ可以在此基础上优化一下,如:把数据访问的代码和加载树结点的方法形成函数,可以实现强制刷新的功能,这在当树结点会不时发生变化的情况下也可以应用。

个人以为这种方法,速度快些,每次加载少量的数据,用户不操作的部分不会去查询这部分的,减少了数据网络传输量。速度也是可以保证的。
=====================================================================
如果不一定要一次性的加载所有数据,建议不要用递归了,查询改成按编码和父编码排序吧,写代码当父结点发生变化时,建立相应的结点。
SQL语句:
select FInterID,FParentID,FName,FNumber from ICBOMGROUP order by FParentID,FInterID 

要保证第一条记录是根结点,如果父结点发生变化,将结点加到相应的位置,有用到结点的查找,如果父结点编号为0,则为树的根结点,否则,要在树的当前结点中查找。
也可以不用SQL的排序,而用DataTable的排序功能来实现这些。

或者,有将递归改成非递归的方法,直接将LZ的方法改成非递归的方法,数据结构教材上有,LZ去看一下。
另外,数据源属性 的get方法,应该在私有变量为空的时候,才去查询数据库吧,而不是取一次值,查一镒数据库,和我的动态加载树结点的情况有些类似。

#1


题目:树型BOM结构递归问题
问题:连接datagridview控件的语句?

#2


先只求根结点,加到树上,然后在单击的时候,展开下层,这样能提高效率,防止一次性加载时间过长,另外代码也好写。在点击树结点时,根据树结点属性,如ID得到其所有子结点的数据,并将其加载到表格中。

#3


先获取FParentID(父BOM内码) = 0的做为根节点.

然后使用循环逐一获取某根节点的各级节点.

C#不会,帮顶.

#4


#5


如果是SQL,参考如下:


/*
标题:查询所有*节点及其子节点的例
地址:http://topic.csdn.net/u/20090323/21/63a91f51-c4df-464d-ba18-64343deb4e3a.html
作者:爱新觉罗·毓华(十八年风雨,守得冰山雪莲花开)
时间:2009-03-23
地点:广东深圳
*/

[code=SQL]create table Area (id int identity,Name varchar(10) ,order_by int ,father_ID int )
insert into area values('广东省',2,0) 
insert into area values('四川省',2,0) 
insert into area values('湖北省',2,0) 
insert into area values('东莞市',1,1) 
insert into area values('广州市',1,1) 
insert into area values('天河区',0,5) 
insert into area values('绵阳市',1,2) 
insert into area values('武汉市',1,3) 
insert into area values('汉口区',0,8) 
insert into area values('随州市',1,3)
go

select * from area

drop table area

/*
id          Name       order_by    father_ID   
----------- ---------- ----------- ----------- 
1           广东省        2           0
2           四川省        2           0
3           湖北省        2           0
4           东莞市        1           1
5           广州市        1           1
6           天河区        0           5
7           绵阳市        1           2
8           武汉市        1           3
9           汉口区        0           8
10          随州市        1           3

(所影响的行数为 10 行)

要求显示为:
name           
-------------- 
广东省
  东莞市
  广州市
    天河区
四川省
  绵阳市
湖北省
  武汉市
    汉口区
  随州市

(所影响的行数为 10 行)
*/



--创建原始表
create table Area (id int identity,Name varchar(10) ,order_by int ,father_ID int )
insert into area values('广东省',2,0) 
insert into area values('四川省',2,0) 
insert into area values('湖北省',2,0) 
insert into area values('东莞市',1,1) 
insert into area values('广州市',1,1) 
insert into area values('天河区',0,5) 
insert into area values('绵阳市',1,2) 
insert into area values('武汉市',1,3) 
insert into area values('汉口区',0,8) 
insert into area values('随州市',1,3)
--创建临时表
create table tmp (id int identity,Name varchar(10) ,order_by int ,father_ID int )
go

--创建查询指定节点及其所有子节点的函数
create function f_cid(@ID int) returns @t_level table(id int , level int)
as
begin
  declare @level int
  set @level = 1
  insert into @t_level select @id , @level
  while @@ROWCOUNT > 0
  begin
    set @level = @level + 1
    insert into @t_level select a.id , @level
    from area a , @t_Level b
    where a.father_ID = b.id and b.level = @level - 1
  end
  return
end
go

--创建存储过程并将数据插入临时表
create proc my_proc 
as
begin
  declare @id as int
  set @id = 0
  while exists(select 1 from area where order_by = 2 and id > @id)
  begin
    set @id = (select min(id) from area where order_by = 2 and id > @id)
    insert into tmp(Name ,order_by ,father_ID) select a.name,a.order_by ,a.father_id from area a , f_cid(@id) b where a.id = b.id order by a.id 
  end
end
go
exec my_proc

--从临时表提取数据并显示
select case when order_by = 2 then name
            when order_by = 1 then '  ' + name
            when order_by = 0 then '    ' + name
       end name
from tmp order by id

drop function f_cid
drop proc my_proc
drop table area , tmp

/*
name           
-------------- 
广东省
  东莞市
  广州市
    天河区
四川省
  绵阳市
湖北省
  武汉市
    汉口区
  随州市

(所影响的行数为 10 行)

*/

#6


谢谢各位了,哎..代码编译通过,就是生成不了数据,郁闷啊...CSDN.C#论坛没几个高手解答

#7


该回复于2009-09-25 10:48:41被版主删除

#8


帮顶

#9


BOM展现为什么使用Gridview,而不使用树形结构。比如说树形菜单。

#10


展阶后要有相关的BOM信息哈,要求显示的信息不同

#11


C#树形结构?

#12


感觉做法没有问题啊,应该是根节点
或者父子节点设定的问题,递归的时候这个挺难调试的
你先不要忙着绑定treeview那边,先递归显示到一个textbox里整理好递归那个函数再绑定treeview了
其实用devexpress有专门的控件

#13


是的。C#树型结构.代码没有任何问题,就是ICBOMGROUP这张表里数据量庞大,调试的时候需要非常长的时间,,寻求简便方法

#14


给个TREEVIEW的例子吧

#15


上面就是在FORM窗体中添加TREEVIEW啊..自动递归添加树节点和根节点的.只是没做到单击一个结点,只是调用这个结点的下一个子结点,而是将所有结点都计算了一次,所以导致数据量庞大...寻求简便方法....

#16


c#不懂,只能帮顶了!

#17


C#,你进的版块不对,这边只针对数据库.

#18


等了几天还是没高手来解答

#19


treeview递归网上有很多C#的代码例

点到treeview结点显示BOM当前层次只用一个datagridview吗?

父件是单一记录,对应子件可能是多条记录,主从表结构
用两个datagridview 或者 多个LABEL 和 一个datagridview吧。

#20


在buildtree过程中,只查询FParentID=0的记录。

public void BuildTree(TreeNodeCollection fNodes, Int32 fParentId)
        {
            SqlConnection thisconnection = new SqlConnection("server=(local);database=GADMEIERP;Integrated Security=true;");
                SqlCommand cmd = thisconnection.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select FInterID,FParentID,FName,FNumber from ICBOMGROUP where
                        FParentID=0 ";
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = cmd;
                DataSet ds = new DataSet();
                adapter.Fill(ds, "ICBOMGROUP");
                this.fSampleDataSource = ds.Tables[0]; 
            //以下填充树
            foreach (DataRow row
                in this.SampleDataSource.Select(String.Format("FParentID = {0}", fParentId)))
            {
                TreeNode node = new TreeNode();
                node.Text = row["FName"].ToString();
                node.Tag = row["FInterID"].ToString();

                tv.Nodes.Add(node);
            }
        } 

加载了所有根结点后,在点击树结点时,查询相应的子结点,并加入树中。

private void tv_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if e.node.nodes.count>0 then //如果有子结点,则不需要查询其子结点
              return;
            string InterID= (string)e.Node.Tag;
             SqlConnection thisconnection = new SqlConnection("server=(local);database=GADMEIERP;Integrated Security=true;");
                SqlCommand cmd = thisconnection.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "select FInterID,FParentID,FName,FNumber from ICBOMGROUP where
                        FParentID='"+InterID+"'";
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = cmd;
                DataSet ds = new DataSet();
                adapter.Fill(ds, "ICBOMGROUP");
                this.fSampleDataSource = ds.Tables[0]; 
            //以下填充树
            foreach (DataRow row
                in this.SampleDataSource.Select(String.Format("FParentID = {0}", fParentId)))
            {
                TreeNode node = new TreeNode();
                node.Text = row["FName"].ToString();
                node.Tag = row["FInterID"].ToString();

                //应该是类似的方法,因为手头没有现成的例子,LZ凑合着看吧。
                e.Node.AddChildNode(node);
                //或e.Node.Nodes.add(node);
            }
           
        } 


代码不一定准确,但基本上这个思路。LZ可以在此基础上优化一下,如:把数据访问的代码和加载树结点的方法形成函数,可以实现强制刷新的功能,这在当树结点会不时发生变化的情况下也可以应用。

个人以为这种方法,速度快些,每次加载少量的数据,用户不操作的部分不会去查询这部分的,减少了数据网络传输量。速度也是可以保证的。
=====================================================================
如果不一定要一次性的加载所有数据,建议不要用递归了,查询改成按编码和父编码排序吧,写代码当父结点发生变化时,建立相应的结点。
SQL语句:
select FInterID,FParentID,FName,FNumber from ICBOMGROUP order by FParentID,FInterID 

要保证第一条记录是根结点,如果父结点发生变化,将结点加到相应的位置,有用到结点的查找,如果父结点编号为0,则为树的根结点,否则,要在树的当前结点中查找。
也可以不用SQL的排序,而用DataTable的排序功能来实现这些。

或者,有将递归改成非递归的方法,直接将LZ的方法改成非递归的方法,数据结构教材上有,LZ去看一下。
另外,数据源属性 的get方法,应该在私有变量为空的时候,才去查询数据库吧,而不是取一次值,查一镒数据库,和我的动态加载树结点的情况有些类似。

#21