Pregunta

Tengo una IEnumerable (de empleado) con una relación ParentID / ChildID con sí mismo que puedo DataBind a un TreeView y puebla la jerarquía perfectamente. Sin embargo, quiero ser capaz de bucle de forma manual a través de todos los registros y crear todos los nodos mediante programación para que pueda cambiar los atributos para cada nodo en base a los datos de ese artículo / no determinada.

¿Hay un tutorial por ahí que explica cómo hacer esto? He visto a muchos que el uso de bases de datos y tablas de datos pero ninguno que muestre cómo hacerlo en LINQ a SQL (IEnumerable)

ACTUALIZACIÓN:

Así es como solía hacerlo con un conjunto de datos -. Me parece que no puede encontrar la manera de hacer lo mismo con 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
¿Fue útil?

Solución

Si sólo tiene una manera de enumerar el árbol se puede implementar esto como un generador, al principio puede parecer extraño, usted es probablemente mejor de un empadronador definido por el usuario, pero es esencialmente la misma cosa.

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;
    }
}

El tipo de restricción, donde TEntity: IGetChildItems es sólo para significar que usted necesita para abstraer cómo desciende la jerarquía. Sin el código anterior no se compilará.

Esto enumerar el árbol en una primera amplitud de la moda, se producirá el primer elemento padre entonces es los niños, y luego los hijos de esos hijos. Usted puede personalizar el código anterior para lograr un comportamiento diferente.

Editar:

La materia de retorno de rendimiento le dice al compilador que debe devolver un valor y luego continuar. rendimiento es una palabra clave contexto y sólo está permitido dentro de una sentencia iterativa. Un generador es una forma sencilla de escribir una fuente de datos IEnumerable. El compilador construirá una máquina de estados de este código y crear una clase anónima enumerable. Al parecer, la palabra clave de rendimiento no existe en VB.NET. Pero todavía se puede escribir una clase que hace esto.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top