ツリービュー、LINQのツーSQL再帰的なデータ母集団
-
09-09-2019 - |
質問
私は、ツリービューにデータバインドすることができ、それ自体とのParentID /たchildID関係に(従業員の)のIEnumerableを有し、それは完全に階層を移入します。しかし、私はすべてのレコードを介して手動でループすることができると私はその所与のアイテム/なし用データに基づいて、各ノードの属性を変更することができるように、プログラムのすべてのノードを作成します。
これを実行する方法について説明しそこチュートリアルはありますか?私は多く見てきたこと、使用データセットとのDataTableが、SQL(IEnumerableを)へのLINQでそれを行う方法を示しなし
UPDATE:
ここで私は、データセットでそれを行うために使用される方法です - 。私はただのIEnumerableと同じことを行う方法を見つけるように見えることはできません。
Private Sub GenerateTreeView()
Dim ds As New DataSet()
Dim tasktree As New Task(_taskID)
Dim dt As DataTable = tasktree.GetTaskTree()
ds.Tables.Add(dt)
ds.Relations.Add("NodeRelation", dt.Columns("TaskID"), dt.Columns("ParentID"))
Dim dbRow As DataRow
For Each dbRow In dt.Rows
If dbRow("TaskID") = _taskID Then
Dim node As RadTreeNode = CreateNode(dbRow("Subject").ToString(), False, dbRow("TaskID").ToString())
RadTree1.Nodes.Add(node)
RecursivelyPopulate(dbRow, node)
End If
Next dbRow
End Sub
Private Sub RecursivelyPopulate(ByVal dbRow As DataRow, ByVal node As RadTreeNode)
Dim childRow As DataRow
Dim StrikeThrough As String = ""
Dim ExpandNode As Boolean = True
For Each childRow In dbRow.GetChildRows("NodeRelation")
Select Case childRow("StatusTypeID")
Case 2
StrikeThrough = "ActiveTask"
Case 3
StrikeThrough = "CompletedTask"
ExpandNode = False
Case 4, 5
StrikeThrough = "ClosedTask"
ExpandNode = False
Case Else
StrikeThrough = "InactiveTask"
ExpandNode = False
End Select
Dim childNode As RadTreeNode = CreateNode("<span class=""" & StrikeThrough & """><a href=""Task.aspx?taskid=" & childRow("TaskID").ToString() & """>" & childRow("Subject").ToString() & "</a></span>", ExpandNode, childRow("TaskID").ToString())
node.Nodes.Add(childNode)
RecursivelyPopulate(childRow, childNode)
ExpandNode = True
Next childRow
End Sub
Private Function CreateNode(ByVal [text] As String, ByVal expanded As Boolean, ByVal id As String) As RadTreeNode
Dim node As New RadTreeNode([text])
node.Expanded = expanded
Return node
End Function
解決
あなただけの木を列挙する方法が必要な場合には発電機としてこれを実装することができ、それは奇妙に見えるかもしれません、あなたはおそらく、ユーザー定義の列挙子とのより良いですが、それは本質的には同じことだ。
public interface IGetChildItems<TEntity>
{
IEnumerable<TEntity> GetChildItems();
}
public static IEnumerable<TEntity> Flatten<TEntity>(TEntity root)
where TEntity : IGetChildItems<TEntity>
{
var stack = new Stack<TEntity>();
stack.Push(root);
while (stack.Count > 0)
{
var item = stack.Pop();
foreach (var child in item.GetChildItems())
{
stack.Push(child);
}
yield return item;
}
}
TEntity型制約:IGetChildItemsは、階層を降りする方法を抽象化する必要があることを意味するだけです。上記のコードなしでコンパイルされないでしょう。
このは幅優先ファッションのツリーを列挙します、それは子供たち、そして子どもたちの子供の最初に親要素が得られます。あなたは簡単に別の動作を実現するために上記のコードをカスタマイズすることができます。
編集:
利回りリターンのものは、それが継続し、その後値を返す必要があり、コンパイラに指示します。収量はコンテキストキーワードであると、それだけ繰り返し文の中許可されています。発電機は、IEnumerableをデータソースを書くの簡単な方法です。コンパイラは、このコードからステートマシンを構築し、列挙匿名クラスを作成します。どうやらyieldキーワードはVB.NETには存在しません。しかし、あなたはまだこれを行うクラスを書くことができます。
Imports System
Imports System.Collections
Imports System.Collections.Generic
Public Class HierarchyEnumerator(Of TEntity As IGetChildItems(Of TEntity))
Implements IEnumerator(Of TEntity), IDisposable, IEnumerator
Public Sub New(ByVal root As TEntity)
Me.stack = New Stack(Of TEntity)
Me.stack.Push(root)
End Sub
Public Sub Dispose()
End Sub
Public Function MoveNext() As Boolean
Do While (Me.stack.Count > 0)
Dim item As TEntity = Me.stack.Pop
Dim child As TEntity
For Each child In item.GetChildItems
Me.stack.Push(child)
Next
Me.current = item
Return True
Loop
Return False
End Function
Public Sub Reset()
Throw New NotSupportedException
End Sub
Public ReadOnly Property Current() As TEntity
Get
Return Me.current
End Get
End Property
Private ReadOnly Property System.Collections.IEnumerator.Current As Object
Get
Return Me.Current
End Get
End Property
Private current As TEntity
Private stack As Stack(Of TEntity)
End Class