树和自联表(一) - 中国WEB开发者网络 (http://www.webasp.net) -- 技术教程 (http://www.webasp.net/article/) --- 树和自联表(一) (http://www.webasp.net/article/29/28002.htm) |
| -- 作者:未知 -- 发布日期: 2006-12-01 |
Author:水如烟
所以,我也尝试一下写写这方面的代码。 通常的,要做成泛型类才能通用。所以,若还是使用.Net FrameWork1.1的话,无法使用下面的类了。 下面是为应用自联表做的树类,只有两个文件: 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) gByProperty = Not uSystem.uReflection.CommonFunction.TypeHasFields(GetType(T)) End Sub Sub New() Initialzie() End Sub Sub New(ByVal item As T) gItem = item Initialzie() End Sub Public Function GetNodeCount(ByVal includeSubNodes As Boolean) As 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="includeSubNodes">是否包括子节点的T对象</param> Public Function ConvertToDataTable(ByVal includeSubNodes As Boolean) As DataTable gTable = uSystem.uReflection.CommonFunction.CreateTableFromType(GetType(T)) If gTable.Columns.Count = 0 Then If gByProperty Then Throw New Exception("对象无属性列") Else Throw New Exception("对象无字段列") End If End If Me.ForEach(New Action(Of T)(AddressOf GetDataTableDatasAction), includeSubNodes) gTable.AcceptChanges() Return gTable End Function Private Sub GetDataTableDatasAction(ByVal item As T) uSystem.uReflection.CommonFunction.ItemAppendToTable(Of T)(item, gTable) End Sub ''' <summary> ''' 将当前节点树转换为TreeNode ''' </summary> ''' <param name="NameOfTreeNodeText">TreeNode的Text值对应的T对象属性名或字段名</param> ''' <param name="includeSubNodes">是否包括子节点</param> ''' <remarks>TreeNode的Tag存T对象值</remarks> Public Function ConvertToTreeNode(ByVal nameOfTreeNodeText As String, ByVal includeSubNodes As Boolean) As Windows.Forms.TreeNode CheckValid(gByProperty, nameOfTreeNodeText) Dim mTreeNode As System.Windows.Forms.TreeNode = ConvertToTreeNode(Me, gByProperty, nameOfTreeNodeText) If includeSubNodes Then AppendTreeNode(mTreeNode, Me, gByProperty, nameOfTreeNodeText) Return mTreeNode End Function Private Shared Sub AppendTreeNode(ByVal treeNode As Windows.Forms.TreeNode, ByVal node As Node(Of T), ByVal byProperty As Boolean, ByVal 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 Boolean, ByVal nameOfTreeNodeText As String) As System.Windows.Forms.TreeNode Dim mTextValue As Object If byProperty Then mTextValue = GetType(T).GetProperty(nameOfTreeNodeText).GetValue(node.Item, Nothing) Else mTextValue = GetType(T).GetField(nameOfTreeNodeText).GetValue(node.Item) End If If mTextValue Is Nothing Then mTextValue = "Root" End If Dim mTreeNode As New System.Windows.Forms.TreeNode(mTextValue.ToString) mTreeNode.Tag = node.Item Return mTreeNode End Function Private Sub CheckValid(ByVal byProperty As Boolean, ByVal 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 Not IsValidIndex(index) 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 Integer, ByVal Value As T) If Not IsValidIndex(index) 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 Integer, ByVal 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 Private Function IsValidIndex(ByVal index As Integer) As Boolean If index >= 0 Then Return index < Me.Count End If Return False End Function End Class End Namespace 两个辅助的类,专用于序列化、反射取值赋值用的。 Namespace LzmTW.uSystem.uRuntime.uSerialization Public Class SerializeHelper Private Sub New() End Sub <System.ComponentModel.EditorBrowsable(System.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(System.ComponentModel.EditorBrowsableState.Advanced)> _ Public Shared Function XmlToItem(Of T)(ByVal xml As String) As 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(System.ComponentModel.EditorBrowsableState.Advanced)> _ Public Shared Sub ItemToXmlFile(Of T)(ByVal filename As String, ByVal 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(System.ComponentModel.EditorBrowsableState.Advanced)> _ Public Shared Function XmlFileToItem(Of T)(ByVal filename As String) As 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(System.ComponentModel.EditorBrowsableState.Advanced)> _ Public Shared Sub ItemToFormatterFile(Of T)(ByVal filename As String, ByVal 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(System.ComponentModel.EditorBrowsableState.Advanced)> _ Public Shared Function FormatterFileToItem(Of T)(ByVal FileName As String, ByVal 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 String, ByVal 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 String, ByVal 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 ReflectionCommonFunction.vb Namespace LzmTW.uSystem.uReflection Public Class CommonFunction Private Sub New() End Sub Public Shared Function TypeHasFields(ByVal t As Type) As Boolean Return t.GetFields.Length > 0 End Function Public Shared Function CreateTableFromType(ByVal t As Type) As DataTable Dim tmpTable As New DataTable If TypeHasFields(t) Then For Each f As Reflection.FieldInfo In t.GetFields tmpTable.Columns.Add(f.Name, f.FieldType) Next Else For Each p As Reflection.PropertyInfo In t.GetProperties If p.CanRead Then tmpTable.Columns.Add(p.Name, p.PropertyType) Next End If Return tmpTable End Function Public Shared Function ItemToDataRow(Of T)(ByVal item |