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) 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
两个辅助的类,专用于序列化、反射取值赋值用的。 SerializeHelper.vb
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 > | |