CSDN博客

img rickjelly2004

树形结构在开发中的应用

发表于2004/7/12 10:27:00  1423人阅读

分类: VB

首先,我们在SQL SERVER 2000里建立一个表tbTree,表的结构设计如下:
列名
数据类型
描述
长度
主键
ID
Int
节点编号
4
ConText
Nvarchar
我们要显示的节点内容
50
 
ParentID
Int
父节点编号
4
 
Depth
Int
深度
4
 
关于Depth(深度)字段,主要是存放节点的层数,也就是说这个节点在树中的哪个层。有Depth(深度)字段,我们编程时会比较方便,在SQL查询时只有加一个where 条件就可以查询出当前深度的层的所有节点。如果我们不设计Depth(深度)字段,同样可以做类似的查询,这就需要在后台的SQL 查询中用循环处理。或者,你可以不在后台数据库服务器端处理,把这些处理放在前台。下面我们将介绍这几种处理方式:
 
在SQL SERVER 2000中建表的脚本:

CREATE TABLE [dbo].[tbTree] (

       [ID] [int] IDENTITY (1, 1) NOT NULL ,

       [Context] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,

       [ParentID] [int] NULL

) ON [PRIMARY]

 
在表中添加如下记录:
 
 

SET IDENTITY_INSERT tbtree ON

insert tbtree (ID,Context,ParentID)  values ( 1,'中国',0)

insert tbtree (ID,Context,ParentID)  values ( 2,'北京',11)

insert tbtree (ID,Context,ParentID)  values ( 3,'天津',1)

insert tbtree (ID,Context,ParentID)  values ( 4,'河北省',1)

insert tbtree (ID,Context,ParentID)  values ( 5,'广东省',1)

insert tbtree (ID,Context,ParentID)  values ( 6,'广州',5)

insert tbtree (ID,Context,ParentID)  values ( 7,'四川省',1)

insert tbtree (ID,Context,ParentID)  values ( 8,'成都',7)

insert tbtree (ID,Context,ParentID)  values ( 9,'深圳',5)

insert tbtree (ID,Context,ParentID)  values ( 10,'石家庄',4)

insert tbtree (ID,Context,ParentID)  values ( 11,'辽宁省',1)

insert tbtree (ID,Context,ParentID)  values ( 12,'大连',11)

insert tbtree (ID,Context,ParentID)  values ( 13,'上海',1)

insert tbtree (ID,Context,ParentID)  values ( 14,'天河软件园',6)

insert tbtree (ID,Context,ParentID)  values ( 15,'汕头',5)

SET IDENTITY_INSERT tbtree off

有Depth(深度)字段时在VB6 中的实现 :

  我们看一下,用ADD方法添加一个新节点到TreeView的节点集合,语法如下:
  Nodes.Add(relative,[relationship][,key][,text][,image][,selectedimage])
从上面的语法,可以看出添加一个节点,只需要知道父节点编号的key,就可以通过这个key添加子节点。
    如果数据库中查询出来的结果集中是按Depth (深度)列排序的话,就可以先加第一层的节点、再加第二层的节点….一直到第N层。所以,下文我写了一个AddTree函数,参数是层数(深度),RS是打开小于等于此层数的所有记录,并按层数排序。所以一层一层地添加,通过循环记录集,就可以完成一颗树。够简单吧!
 
 

Dim CN As ADODB.Connection                '定义数据库的连接

Dim Rs As ADODB.Recordset

 

'工程--->引用--->Microsoft ActiveX Data Object 2.x(版本号)

Private Sub Form_Load()

    Set CN = New ADODB.Connection

       连接数据库

    CN.ConnectionString = "Provider=sqloledb;Data Source=pmserver;Initial Catalog=Benchmark;User Id=sa;Password=sa;"

    CN.Open

Call AddTree(3)

End Sub

 

Private Sub AddTree(ByVal intDepth As Integer)

       打开记录集,得到深度小于些深度的所有节点,并按深度排序

    Set Rs = New ADODB.Recordset

    Rs.Open "select * from tbTree where depth<='" & intDepth & "' order by depth", CN, adOpenDynamic, adLockReadOnly

    Dim Xnod As Node

    Do While Not Rs.EOF

        If Rs.Fields("depth") = 0 Then

                     加入根结点

            Set Xnod = TreeView1.Nodes.Add(, , "key" & Rs.Fields("id"), Rs.Fields("context"))

        Else

                     加入子节点

            Set Xnod = TreeView1.Nodes.Add("key" & Rs.Fields("parentid"), tvwChild, "key" & Rs.Fields("id"), Rs.Fields("context"))

        End If

        Xnod.EnsureVisible

        Rs.MoveNext

    Loop

    Rs.Close

End Sub

 
程序运行结果如下图所示:
 

没有Depth(深度)时的实现

上面的程序完全是依靠Depth这一列,如果没有深度这一列来排序,可以看出,上面的代码就会出错!
从tbTree表的设计可以看出,如果没有Depth这一列,只要有ID字段和ParentID字段就可以查询到一个节点下的所有节点,答案是肯定的!看我们下面这个存储过程,其作用就是你只要传一个ID号,就可以找出下面的所有节点!而且这些节点是按层次排序的!
 
建立存储过程:

CREATE PROCEDURE  spGetTree (

       @ID int)

as

set nocount on

declare @tmp table (Id int,ConText varchar(50),ParentID int,depth int)

insert @tmp select * from tbtree where ID=@ID

while exists(select 1 from tbtree a,@tmp b where a.ParentID=b.ID and a.ID not in (select ID from @tmp))

  insert @tmp select a.* from  tbtree a,@tmp b where a.ParentID=b.ID and a.ID not in (select ID from @tmp)

select * from @tmp

set nocount off

GO

 

剖析:上面的存储过程,While语句就是一层一层地将地将树的节点插入到目的表@tmp中。有兴趣的读者可以自行跟踪一下。
 
我们利用上面这个存储过程,可以很容易地用VB6写出添加树状结构的代码,因为这个存储过程得到的数据是已经按层次排好序的,我们只要循环记录集,顺序添加节点就可以。
 

Private Sub AddTreeEx(ByVal intID As Integer)

    Set Rs = New ADODB.Recordset

    Rs.Open "spGettree " & intID, CN, adOpenDynamic, adLockReadOnly

    Dim Xnod As Node

    Do While Not Rs.EOF

        If Rs.Fields("parentID") = 0 Then

            Set Xnod = TreeView1.Nodes.Add(, , "key" & Rs.Fields("id"), Rs.Fields("context"))

        Else

            Set Xnod = TreeView1.Nodes.Add("key" & Rs.Fields("parentid"), tvwChild, "key" & Rs.Fields("id"), Rs.Fields("context"))

        End If

        Xnod.EnsureVisible

        Rs.MoveNext

    Loop

    Rs.Close

End Sub

 

在VB.NET中实现
    在.NET中,由于TreeView控件的用法和VB6中的用法是不一样的!以前的VB6程序员会因为节点没有Key属性而烦恼!在.NET中,TreeView树的节点是一个集合,每个 TreeNode 都可以包含其他 TreeNode 对象的集合。要确定您在树结构中的位置,得使用 FullPath 属性。
    我们知道,添加节点只能是在找到节点之后再此节点下添加。现在VB.NET少了Key属性,对操作是一个很大的不便。微软MSDN有一篇文章用继承和重载的方法,扩展了TreeView控件,给节点加了一个key属性。有兴趣的读者可以看一下HOW TO:Create a Key Property for a TreeView Node in Visual Basic .NET这篇文章。但是美中不足的是:这篇文章只是为TreeNode加了一个NodeKey属性,但是没有提供好的Key值检索功能。尽管这一切我们都可以用代码来扩展,但是代码冗长。
    所以,添加许多层节点的树形结构,只能是递归调用。而且,我们下面的代码很精炼,只需要传给递归过程一个ParentID,就会将这个编号下的所有节点加载到树形结构中!充分体现了:简单就是好的思想。
    设计思想:从数据库中查询到所有节点的记录,添加到DataView中,利用DataView的.RowFilter属性得到某个父节点编号ParentID下的所有记录,依次递归循环。
 
在VB.net中实现:

    Private ds As New DataSet ()

' AddTree递归函数每次都要用到数据集中的一个表,所以定义成private

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' '定义数据库连接

        Dim CN As New SqlConnection()

        Try

          '初始化连接字符串

            CN.ConnectionString = "data source=pmserver;initial catalog=Benchmark;persist security info=False;user id=sa;Password=sa;"

            CN.Open()

                '添加命令,从数据库中得到数据

            Dim sqlCmd As New SqlCommand()

            sqlCmd.Connection = CN

            sqlCmd.CommandText = "select * from tbtree"

            sqlCmd.CommandType = CommandType.Text

            Dim adp As SqlDataAdapter = New SqlDataAdapter(sqlCmd)

            adp.Fill(ds)

        Catch ex As Exception

            MsgBox(ex.Message)

        Finally

          '关闭连接

            CN.Close()

        End Try

        '调用递归函数,完成树形结构的生成

        AddTree(0, Nothing)

    End Sub

 

    '?递归添加树的节点

    Private Sub AddTree(ByVal ParentID As Integer, ByVal pNode As TreeNode)

        Dim Node As TreeNode

        Dim dvTree As New DataView()

        dvTree = New DataView(ds.Tables(0))

        '过滤ParentID,得到当前的所有子节点

        dvTree.RowFilter = "PARENTID = " + ParentID.ToString

 

        Dim Row As DataRowView

        For Each Row In dvTree

            If pNode Is Nothing Then  '判断是否根节点

                '?添加根节点

                Node = TreeView1.Nodes.Add(Row("context").ToString())

                            '?再次递归

                AddTree(Int32.Parse(Row("ID").ToString()), Node)

            Else

                添加当前节点的子节点

                Node = pNode.Nodes.Add(Row("context").ToString())

                            '?再次递归

                AddTree(Int32.Parse(Row("ID").ToString()), Node)

            End If

            Node.EnsureVisible()

        Next

    End Sub

程序运行结果如下图所示:
 

在C# 中实现:

       有了在VB.NET中实现的代码,我们只要改成C#的语法就可以了:
               DataSet ds=new DataSet();
              private void Form1_Load(object sender, System.EventArgs e)
              {
                     // 定义数据库连接
                     SqlConnection CN = new SqlConnection();
                     try
                     {
                            //初始化连接字符串
                            CN.ConnectionString= "data source=pmserver;initial catalog=Benchmark;persist security info=False;user id=sa;Password=sa;";
                            CN.Open();
                            //添加命令,从数据库中得到数据
                            SqlCommand sqlCmd= new SqlCommand();
                            sqlCmd.Connection = CN;
                            sqlCmd.CommandText = "select * from tbTree";
                            sqlCmd.CommandType = CommandType.Text ;
                            SqlDataAdapter adp = new SqlDataAdapter(sqlCmd);
                            adp.Fill(ds);
                     }
                     catch (Exception ex)
                     {
                            throw (ex);  
                     }
                     finally
                     {
                            CN.Close();
                     }
                     //调用递归函数,完成树形结构的生成
                     AddTree(0, (TreeNode)null);
              }
 
              // 递归添加树的节点
              public void AddTree(int ParentID,TreeNode pNode)
              {
                     DataView dvTree = new DataView(ds.Tables[0]);
                     //过滤ParentID,得到当前的所有子节点
                     dvTree.RowFilter =  "[PARENTID] = " + ParentID;
                     foreach(DataRowView Row in dvTree)
                     {
                            if(pNode == null)
                            {    //'?添加根节点
                                   TreeNode Node = treeView1.Nodes.Add(Row["ConText"].ToString());
                                   AddTree(Int32.Parse(Row["ID"].ToString()),Node);    //再次递归
                            }
                            else
                            {   //添加当前节点的子节点
                                   TreeNode Node =  pNode.Nodes.Add(Row["ConText"].ToString());
                                   AddTree(Int32.Parse(Row["ID"].ToString()),Node);     //再次递归
                            }
                     }                  
              }           
 
后记:请读者自行修改程序中的连接字符串设置。
附:相关微软MSDN文档,包括在VB6和.NET中从XML建立树形结构
0 0

相关博文

我的热门文章

img
取 消
img