TreeView、Linq-To-SQL 递归数据填充

发布于 2024-07-13 19:27:37 字数 2431 浏览 9 评论 0原文

我有一个 IEnumerable(of Employee),其自身具有 ParentID/ChildID 关系,我可以将其数据绑定到 TreeView,并且它完美地填充层次结构。 但是,我希望能够手动循环所有记录并以编程方式创建所有节点,以便我可以根据给定项目/无的数据更改每个节点的属性。

有没有教程解释如何做到这一点? 我见过很多使用数据集和数据表的方法,但没有一个显示如何在 Linq to SQL (IEnumerable) 中执行此

操作更新:

这是我以前使用数据集执行此操作的方法 - 我似乎找不到如何执行此操作与 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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半仙 2024-07-20 19:27:37

如果您只需要一种枚举树的方法,您可以将其实现为生成器,它可能看起来很奇怪,您可能更适合使用用户定义的枚举器,但它本质上是相同的事情。

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 的类型约束只是表示您需要抽象如何降低层次结构。 如果没有上面的代码就无法编译。

这将以广度优先的方式枚举树,它将首先产生父元素,然后是它的子元素,然后是这些子元素的子元素。 您可以轻松地自定义上述代码以实现不同的行为。

编辑:

yield 返回的东西告诉编译器它应该返回一个值然后继续。 Yield 是一个上下文关键字,仅允许在迭代语句中使用。 生成器是编写 IEnumerable 数据源的简单方法。 编译器将根据此代码构建一个状态机并创建一个可枚举的匿名类。 显然VB.NET中不存在yield关键字。 但您仍然可以编写一个类来执行此操作。

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文