Question

J'ai un IEnumerable (de l'employé) avec une relation ParentID / ChildID avec lui-même que je peux DataBind à un TreeView et il remplit la hiérarchie parfaitement. Cependant, je veux être en mesure de boucler manuellement à travers tous les enregistrements et créer tous les nœuds par programme afin que je puisse modifier les attributs pour chaque noeud sur la base des données pour cet élément / aucune donnée.

Y at-il un tutoriel là-bas qui explique comment faire cela? J'ai vu beaucoup que les ensembles de données d'utilisation et datatables mais aucun de ceux qui montrent comment le faire dans LINQ to SQL (IEnumerable)

Mise à jour:

Voici comment je l'habitude de le faire avec un DataSet -. Je ne peux pas sembler trouver comment faire la même chose avec 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
Était-ce utile?

La solution

Si vous avez juste besoin d'un moyen de l'arbre que vous énumérer pouvez implémenter cela comme un générateur, cela peut paraître étrange, vous êtes probablement mieux avec un recenseur défini par l'utilisateur, mais il est essentiellement la même chose.

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

La contrainte de type où TEntity: IGetChildItems est juste pour signifier que vous avez besoin de faire abstraction comment descendre la hiérarchie. Sans le code ci-dessus ne compilerait pas.

énumérera l'arbre dans une largeur d'abord la mode, elle donnera l'élément parent d'abord, puis il est des enfants, puis les enfants de ces enfants. Vous pouvez facilement personnaliser le code ci-dessus pour obtenir un comportement différent.

Edit:

Le truc yield return indique au compilateur qu'il doit renvoyer une valeur puis continuer. le rendement est un mot-clé de contexte et il est seulement permis dans une déclaration itérative. Un générateur est un moyen simple d'écrire une source de données IEnumerable. Le compilateur va construire une machine d'état à partir de ce code et créer une dénombrable classe anonyme. Apparemment, le mot-clé yield n'existe pas dans VB.NET. Mais vous pouvez toujours écrire une classe qui fait cela.

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top