分享
 
 
 

树和自联表(一)

王朝other·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

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 > 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 As T, ByVal table As DataTable) As DataRow

Dim tmpRow As DataRow = table.NewRow

Dim mName As String

Dim mType As Type = GetType(T)

For Each c As DataColumn In table.Columns

mName = c.ColumnName

If TypeHasFields(mType) Then

tmpRow(mName) = mType.GetField(mName).GetValue(item)

Else

tmpRow(mName) = mType.GetProperty(mName).GetValue(item, Nothing)

End If

Next

Return tmpRow

End Function

Public Shared Sub ItemAppendToTable(Of T)(ByVal item As T, ByVal table As DataTable)

table.Rows.Add(ItemToDataRow(Of T)(item, table))

End Sub

Public Shared Sub ItemAppendToTable(Of T)(ByVal items() As T, ByVal table As DataTable)

For Each item As T In items

ItemAppendToTable(Of T)(item, table)

Next

End Sub

Public Shared Function ItemsToTable(Of T)(ByVal items() As T) As DataTable

Dim mTable As DataTable = CreateTableFromType(GetType(T))

If items Is Nothing Then Return mTable

ItemAppendToTable(Of T)(items, mTable)

Return mTable

End Function

End Class

End Namespace

现在可以测试一下。

测试代码:

Public Class Form1

Private gNode As New LzmTW.uSystem.uCollection.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(1, New 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(True, True)

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 String, ByVal [declare] As String)

gName = name

gDeclare = [declare]

End Sub

End Class

效果:

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有