水如烟

                 顺其自然,水到渠成 LzmTW

文或代码皆是面向初学者.我是爱好者,也是初学者.那些"文章",只按自己理解写,我是不知术语名词的.所以只供参考,也仅供参考.

导航

树和自联表(一)

Posted on 2006-11-07 09:51  水如烟(LzmTW)  阅读(411)  评论(0编辑  收藏  举报

Author:水如烟 


自联表我们经常用到,它总是跟树联结在一起。
对于它们的处理,.NET没有专门的处理类。
控件类TreeNode,也没有直接跟自联表挂上钩。

所以,我也尝试一下写写这方面的代码。
如我以往所写的一样,仅提供一种方法,至于更好的方法,更好的效率,鉴于自己学识所限,不深究。

通常的,要做成泛型类才能通用。所以,若还是使用.Net FrameWork1.1的话,无法使用下面的类了。

下面是为应用自联表做的树类,只有两个文件:
Node.vb

 

Namespace LzmTW.uSystem.uCollection

    
<Serializable()> _
    
Public Class Node(Of T)

        
Friend gIsRoot As Boolean = True
        
Friend gParent As Node(Of T)

        
''' <summary>
        ''' 当前节点的父节点
        ''' </summary>
        Public ReadOnly Property Parent() As Node(Of T)
            
Get
                
If Me.IsRoot Then
                    
Return Nothing
                
End If
                
Return gParent
            
End Get
        
End Property

        
''' <summary>
        ''' 树的深度
        ''' </summary>
        Public ReadOnly Property Level() As Integer
            
Get
                
If Me.IsRoot Then
                    
Return 0
                
End If
                
Return Me.Parent.Level + 1
            
End Get
        
End Property


        
''' <summary>
        ''' 当前节点是否是根节点
        ''' </summary>
        Public ReadOnly Property IsRoot() As Boolean
            
Get
                
Return gIsRoot
            
End Get
        
End Property

        
Private gUserData As Object

        
''' <summary>
        ''' 获取或设置包含树节点有关数据的对象
        ''' </summary>
        Public Property Tag() As Object
            
Get
                
Return gUserData
            
End Get
            
Set(ByVal value As Object)
                gUserData 
= value
            
End Set
        
End Property

        
Private gItem As T
        
Public Property Item() As T
            
Get
                
Return gItem
            
End Get
            
Set(ByVal value As T)
                gItem 
= value
            
End Set
        
End Property


        
Friend gChildren As NodeCollection(Of T)

        
''' <summary>
        ''' 获取第一个子树节点
        ''' </summary>
        Public ReadOnly Property FirstNode() As Node(Of T)
            
Get
                
If gChildren.Count = 0 Then
                    
Return Nothing
                
End If
                
Return gChildren(0)
            
End Get
        
End Property

        
''' <summary>
        ''' 获取最后一个子树节点
        ''' </summary>
        Public ReadOnly Property LastNode() As Node(Of T)
            
Get
                
If gChildren.Count = 0 Then
                    
Return Nothing
                
End If
                
Return gChildren(gChildren.Count - 1)
            
End Get
        
End Property


        
Private gNodes As NodeCollection(Of T)

        
''' <summary>
        ''' 当前节点的节点集合
        ''' </summary>
        Public ReadOnly Property Nodes() As NodeCollection(Of T)
            
Get
                
Return gNodes
            
End Get
        
End Property

        
''' <summary>
        ''' 当前节点在节点集合中的位置
        ''' </summary>
        Public ReadOnly Property Index() As Integer
            
Get
                
Return GetIndex()
            
End Get
        
End Property

        
Private Function GetIndex() As Integer
            
If Me.IsRoot Then
                
Return 0
            
End If

            
Return Me.Parent.Nodes.IndexOf(Me)
        
End Function

        
''' <summary>
        ''' 获取下一个同级树节点
        ''' </summary>
        Public ReadOnly Property NextNode() As Node(Of T)
            
Get
                
If Me.IsRoot OrElse Me.Index + 1 > Me.Parent.Nodes.Count Then
                    
Return Nothing
                
End If

                
Return Me.Parent.Nodes.Item(Me.Index + 1)
            
End Get
        
End Property

        
''' <summary>
        ''' 获取上一个同级树节点
        ''' </summary>
        Public ReadOnly Property PrevNode() As Node(Of T)
            
Get
                
If Me.IsRoot OrElse Me.Index - 1 < 0 Then
                    
Return Nothing
                
End If

                
Return Me.Parent.Nodes.Item(Me.Index - 1)
            
End Get
        
End Property

        
Private Sub Initialzie()
            gNodes 
= New NodeCollection(Of T)(Me)
            gChildren 
= New NodeCollection(Of T)(Me)
        
End Sub

        
Sub New()
            Initialzie()
        
End Sub

        
Sub New(ByVal item As T)
            gItem 
= item

            Initialzie()
        
End Sub

        
Public Function GetNodeCount(ByVal includeSubNodes As BooleanAs Integer
            
Dim mCount As Integer = gChildren.Count
            
If includeSubNodes Then
                
Dim mIndex As Integer = 0
                
Do While (mIndex < gChildren.Count)
                    mCount 
+= gChildren(mIndex).GetNodeCount(True)
                    mIndex 
+= 1
                
Loop
            
End If

            
Return mCount
        
End Function

        
Public Sub Remove()
            
If Me.IsRoot Then
                
Throw New Exception("不能移除根节点")
            
End If
            
Me.Parent.Nodes.RemoveAt(Me.Index)
        
End Sub


        
Private gTable As DataTable
        
Private gByProperty As Boolean

        
''' <summary>
        ''' 将当前节点树转换为表
        ''' </summary>
        ''' <param name="byProperty">取T对象的属性集,否则取T对象的字段集作为表列名</param>
        ''' <param name="includeSubNodes">是否包括子节点的T对象</param>
        Public Function ConvertToDataTable(ByVal byProperty As BooleanByVal includeSubNodes As BooleanAs DataTable
            gTable 
= GetDataTable(byProperty)

            
If gTable.Columns.Count = 0 Then
                
If byProperty Then
                    
Throw New Exception("对象无属性列")
                
Else
                    
Throw New Exception("对象无字段列")
                
End If
            
End If

            gByProperty 
= byProperty

            
Me.ForEach(New Action(Of T)(AddressOf GetDataTableDatasAction), includeSubNodes)

            gTable.AcceptChanges()

            
Return gTable
        
End Function

        
Private Function GetDataTable(ByVal byProperty As BooleanAs DataTable
            
Dim tmpTable As New DataTable

            
If byProperty Then
                
For Each p As Reflection.PropertyInfo In GetType(T).GetProperties
                    
If p.CanRead Then tmpTable.Columns.Add(p.Name, p.PropertyType)
                
Next
            
Else
                
For Each f As Reflection.FieldInfo In GetType(T).GetFields
                    tmpTable.Columns.Add(f.Name, f.FieldType)
                
Next
            
End If

            
Return tmpTable
        
End Function

        
Private Sub GetDataTableDatasAction(ByVal item As T)
            
Dim tmpRow As DataRow = gTable.NewRow

            
Dim mName As String

            
For Each c As DataColumn In gTable.Columns
                mName 
= c.ColumnName

                
If gByProperty Then
                    tmpRow(mName) 
= GetType(T).GetProperty(mName).GetValue(item, Nothing)
                
Else
                    tmpRow(mName) 
= GetType(T).GetField(mName).GetValue(item)
                
End If
            
Next

            gTable.Rows.Add(tmpRow)
        
End Sub

        
''' <summary>
        ''' 将当前节点树转换为TreeNode
        ''' </summary>
        ''' <param name="byProperty">取T对象的属性集,否则取T对象的字段集</param>
        ''' <param name="NameOfTreeNodeText">TreeNode的Text值对应的T对象属性名或字段名</param>
        ''' <param name="includeSubNodes">是否包括子节点</param>
        ''' <remarks>TreeNode的Tag存T对象值</remarks>
        Public Function ConvertToTreeNode(ByVal byProperty As BooleanByVal nameOfTreeNodeText As StringByVal includeSubNodes As BooleanAs Windows.Forms.TreeNode
            CheckValid(byProperty, nameOfTreeNodeText)

            
Dim mTreeNode As System.Windows.Forms.TreeNode = ConvertToTreeNode(Me, byProperty, nameOfTreeNodeText)

            
If includeSubNodes Then AppendTreeNode(mTreeNode, Me, byProperty, nameOfTreeNodeText)

            
Return mTreeNode
        
End Function

        
Private Shared Sub AppendTreeNode(ByVal treeNode As Windows.Forms.TreeNode, ByVal node As Node(Of T), ByVal byProperty As BooleanByVal nameOfTreeNodeText As String)
            
For Each n As Node(Of T) In node.gChildren

                
Dim mCurrentTreeNode As Windows.Forms.TreeNode = ConvertToTreeNode(n, byProperty, nameOfTreeNodeText)

                treeNode.Nodes.Add(mCurrentTreeNode)

                AppendTreeNode(mCurrentTreeNode, n, byProperty, nameOfTreeNodeText)

            
Next

        
End Sub

        
Private Shared Function ConvertToTreeNode(ByVal node As Node(Of T), ByVal byProperty As BooleanByVal nameOfTreeNodeText As StringAs System.Windows.Forms.TreeNode
            
Dim mTextValue As String

            
If byProperty Then
                mTextValue 
= GetType(T).GetProperty(nameOfTreeNodeText).GetValue(node.Item, Nothing).ToString
            
Else
                mTextValue 
= GetType(T).GetField(nameOfTreeNodeText).GetValue(node.Item).ToString
            
End If

            
Dim mTreeNode As New System.Windows.Forms.TreeNode(mTextValue)
            mTreeNode.Tag 
= node.Item

            
Return mTreeNode
        
End Function

        
Private Sub CheckValid(ByVal byProperty As BooleanByVal nameOfTreeNodeText As String)
            
If byProperty Then
                
Dim mPropertyInfo As System.Reflection.PropertyInfo = GetType(T).GetProperty(nameOfTreeNodeText)
                
If mPropertyInfo Is Nothing Then
                    
Throw New Exception("属性名无效")
                    
If Not mPropertyInfo.CanRead Then
                        
Throw New Exception("属性名不可读")
                    
End If
                
End If
            
Else
                
Dim mFieldInfo As System.Reflection.FieldInfo = GetType(T).GetField(nameOfTreeNodeText)
                
If mFieldInfo Is Nothing Then
                    
Throw New Exception("字段名无效")
                
End If
            
End If
        
End Sub


        
''' <summary>
        ''' 对每个节点执行指定操作
        ''' </summary>
        ''' <param name="action">对指定的对象执行操作的方法</param>
        ''' <param name="includeSubNodes">是否包括子节点</param>
        Public Sub ForEach(ByVal action As Action(Of Node(Of T)), ByVal includeSubNodes As Boolean)
            Node(Of T).ForEach(
Me, action, includeSubNodes)
        
End Sub

        
Public Shared Sub ForEach(ByVal node As Node(Of T), ByVal action As Action(Of Node(Of T)), ByVal includeSubNodes As Boolean)
            
For Each n As Node(Of T) In node.gChildren
                action.Invoke(n)

                
If includeSubNodes Then ForEach(n, action, True)
            
Next
        
End Sub

        
''' <summary>
        ''' 对每个T对象执行指定操作
        ''' </summary>
        ''' <param name="action">对指定的对象执行操作的方法</param>
        ''' <param name="includeSubNodes">是否包括子节点的T对象</param>
        Public Sub ForEach(ByVal action As Action(Of T), ByVal includeSubNodes As Boolean)
            Node(Of T).ForEach(
Me, action, includeSubNodes)
        
End Sub

        
Public Shared Sub ForEach(ByVal node As Node(Of T), ByVal action As Action(Of T), ByVal includeSubNodes As Boolean)
            
For Each n As Node(Of T) In node.gChildren
                action.Invoke(n.Item)


                
If includeSubNodes Then ForEach(n, action, True)
            
Next
        
End Sub

        
Public Function Clone() As Node(Of T)
            
Return uSystem.uRuntime.uSerialization.SerializeHelper.Clone(Of Node(Of T))(Me)
        
End Function

    
End Class

End Namespace

NodeCollection.vb

 

Namespace LzmTW.uSystem.uCollection

    
<Serializable()> _
    
Public Class NodeCollection(Of T)
        
Inherits System.Collections.ObjectModel.Collection(Of Node(Of T))

        
Private gOwner As Node(Of T)

        
Friend Sub New(ByVal node As Node(Of T))
            gOwner 
= node
        
End Sub

        
Public Shadows Function Add(ByVal Value As T) As Node(Of T)
            
Dim mNode As New Node(Of T)(Value)

            Add(mNode)
            gOwner.gChildren.Add(mNode)

            
Return mNode
        
End Function

        
Private Shadows Sub Add(ByVal item As Node(Of T))
            
With item
                .gParent 
= gOwner
                .gIsRoot 
= False
            
End With

            
MyBase.Add(item)
        
End Sub

        
Public Shadows Sub RemoveAt(ByVal index As Integer)
            
If index < 0 OrElse index > Me.Count - 1 Then
                
Throw New Exception("索引无效")
            
End If

            
Dim mNode As Node(Of T) = Me.Item(index)
            Remove(mNode)

            gOwner.gChildren.Remove(mNode)
        
End Sub

        
Public Shadows Sub Remove(ByVal index As Integer)
            
Me.RemoveAt(index)
        
End Sub

        
Private Shadows Function Remove(ByVal item As Node(Of T)) As Boolean
            
Return MyBase.Remove(item)
        
End Function

        
Public Shadows Sub Insert(ByVal index As IntegerByVal Value As T)
            
If index < 0 OrElse index > Me.Count - 1 Then
                
Throw New Exception("索引无效")
            
End If

            
Dim mNode As New Node(Of T)(Value)

            Insert(index, mNode)
            gOwner.gChildren.Insert(index, mNode)
        
End Sub

        
Private Shadows Sub Insert(ByVal index As IntegerByVal item As Node(Of T))
            
With item
                .gParent 
= gOwner
                .gIsRoot 
= False
            
End With

            
MyBase.Insert(index, item)
        
End Sub

        
Public Overloads Sub Clear()
            
MyBase.Clear()
            
If gOwner.gChildren.Count > 0 Then gOwner.gChildren.Clear()
        
End Sub

    
End Class
End Namespace

一个辅助的类,专用于序列化用的。
SerializeHelper.vb

 

Namespace LzmTW.uSystem.uRuntime.uSerialization

    
Public Class SerializeHelper

        
Private Sub New()
        
End Sub

        
<System.ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)> _
        
Public Shared Function ItemToXml(Of T)(ByVal obj As T) As String
            
Dim mResult As String = ""
            
Dim mSerializer As New System.Xml.Serialization.XmlSerializer(GetType(T))
            
Dim mStringWriter As New System.IO.StringWriter
            Using mStringWriter
                mSerializer.Serialize(mStringWriter, obj)
                mResult 
= mStringWriter.ToString
                mStringWriter.Close()
            
End Using
            
Return mResult
        
End Function

        
<System.ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)> _
        
Public Shared Function XmlToItem(Of T)(ByVal xml As StringAs T
            
Dim mSerializer As New System.Xml.Serialization.XmlSerializer(GetType(T))
            
Dim mStringReader As New System.IO.StringReader(xml)
            
Return CType(mSerializer.Deserialize(mStringReader), T)
        
End Function

        
<System.ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)> _
        
Public Shared Sub ItemToXmlFile(Of T)(ByVal filename As StringByVal obj As T)
            
Dim XmlWriter As New System.IO.StreamWriter(filename, False, System.Text.Encoding.Default)
            Using XmlWriter
                XmlWriter.Write(ItemToXml(obj))
                XmlWriter.Close()
            
End Using
        
End Sub

        
<System.ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)> _
        
Public Shared Function XmlFileToItem(Of T)(ByVal filename As StringAs T
            
Dim XmlReader As New System.IO.StreamReader(filename, System.Text.Encoding.Default)
            
Dim mObj As T
            Using XmlReader
                mObj 
= XmlToItem(Of T)(XmlReader.ReadToEnd)
                XmlReader.Close()
            
End Using
            
Return mObj
        
End Function

        
<System.ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)> _
        
Public Shared Sub ItemToFormatterFile(Of T)(ByVal filename As StringByVal formatter As System.Runtime.Serialization.IFormatter, ByVal obj As T)
            
Dim mFileStream As System.IO.Stream = System.IO.File.Open(filename, System.IO.FileMode.Create)
            Using mFileStream
                formatter.Serialize(mFileStream, obj)
                mFileStream.Close()
            
End Using
        
End Sub

        
<System.ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)> _
        
Public Shared Function FormatterFileToItem(Of T)(ByVal FileName As StringByVal formatter As System.Runtime.Serialization.IFormatter) As T
            
Dim mFileStream As System.IO.Stream = System.IO.File.Open(FileName, System.IO.FileMode.Open)
            
Dim mObj As T
            Using mFileStream
                mObj 
= CType(formatter.Deserialize(mFileStream), T)
                mFileStream.Close()
            
End Using
            
Return mObj
        
End Function

        
Public Shared Function Clone(Of T)(ByVal obj As T) As T
            
Dim tmpT As T
            
Dim mFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            
Dim mMemoryStream As New System.IO.MemoryStream
            Using mMemoryStream
                mFormatter.Serialize(mMemoryStream, obj)
                mMemoryStream.Position 
= 0
                tmpT 
= CType(mFormatter.Deserialize(mMemoryStream), T)
                mMemoryStream.Close()
            
End Using
            
Return tmpT
        
End Function

        
Public Shared Sub Save(Of T)(ByVal filename As StringByVal formattype As FormatType, ByVal obj As T)
            
Select Case formattype
                
Case formattype.Binary
                    ItemToFormatterFile(filename, 
New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter, obj)
                
Case formattype.Soap
                    ItemToFormatterFile(filename, 
New System.Runtime.Serialization.Formatters.Soap.SoapFormatter, obj)
                
Case formattype.Xml
                    ItemToXmlFile(filename, obj)
            
End Select
        
End Sub

        
Public Shared Function Load(Of T)(ByVal filename As StringByVal formattype As FormatType) As T
            
Select Case formattype
                
Case formattype.Binary
                    
Return FormatterFileToItem(Of T)(filename, New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter)
                
Case formattype.Soap
                    
Return FormatterFileToItem(Of T)(filename, New System.Runtime.Serialization.Formatters.Soap.SoapFormatter)
                
Case formattype.Xml
                    
Return XmlFileToItem(Of T)(filename)
            
End Select
            
Return Nothing
        
End Function

    
End Class

    
Public Enum FormatType
        Xml
        Binary
        Soap
    
End Enum

End Namespace


现在可以测试一下。
测试代码:

Public Class Form1

    
Private gNode As New uSystem.uCollection.SinceLink.Node(Of item)(New item("Root"))

    
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        gNode.Nodes.add(
New item("First"))
        gNode.Nodes.Add(
New item("Second")).Nodes.Add(New item("Four")).Nodes.Add(New item("Five")).Nodes.Add(New item("Seven"))
        gNode.Nodes.Add(
New item("Third"))
        gNode.Nodes.Insert(
1New item("Six"))

    
End Sub

    
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        
Me.TreeView1.Nodes.Add(gNode.ConvertToTreeNode(True"Name"True))
        
Me.DataGridView1.DataSource = gNode.ConvertToDataTable(TrueTrue)
    
End Sub

End Class

<Serializable()> _
Public Class item
    
Private gName As String
    
Private gDeclare As String

    
Public Property Name() As String
        
Get
            
Return gName
        
End Get
        
Set(ByVal value As String)
            gName 
= value
        
End Set
    
End Property

    
Public Property [Declare]() As String
        
Get
            
Return gDeclare
        
End Get
        
Set(ByVal value As String)
            gDeclare 
= value
        
End Set
    
End Property

    
Sub New()
    
End Sub

    
Sub New(ByVal name As String)
        gName 
= name
        gDeclare 
= name
    
End Sub

    
Sub New(ByVal name As StringByVal [declareAs String)
        gName 
= name
        gDeclare 
= [declare]
    
End Sub

End Class

效果: