I have an IEnumerable(of Employee) with a ParentID/ChildID relationship with itself that I can databind to a TreeView and it populates the hierarchy perfectly. However, I want to be able to manually loop through all the records and create all the nodes programmatically so that I can change the attributes for each node based on the data for that given item/none.
Is there a tutorial out there that explains how to do this? I've seen many that use datasets and datatables but none that show how to do it in Linq to SQL (IEnumerable)
UPDATE:
Here's how I used to do it with a DataSet - I just can't seem to find how to do the same with 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
-
If you just need a way of enumerating the tree you can implement this as a generator, it might look strange, you're probably better of with a user defined enumerator but it's essentially the same thing.
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; } }
The type constraint where TEntity : IGetChildItems is just to signify that you need to abstract how to descend the hierarchy. Without the above code would not compile.
This will enumerate the tree in a breadth first fashion, it will yield the parent element first then it's children, and then the children of those children. You can easily customize the above code to achieve a different behavior.
Edit:
The yield return stuff tells the compiler that it should return a value then continue. yield is a context keyword and it's only allowed inside an iterative statement. A generator is a simple way of writing a IEnumerable data source. The compiler will build a state machine from this code and create an enumerable anonymous class. Apparently the yield keyword does not exist in VB.NET. But you can still write a class which does this.
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
EdenMachine : What is "yield"? I don't think VB.NET has an equivalent.EdenMachine : I added my previous code because I couldn't make heads or tails of what you were suggesting and how that could apply to what I'm trying to accomplish. I hope that helps clarify my question.
0 comments:
Post a Comment