从 C# 中的分层数据创建 HTML 无序列表

Create HTML unordered list from hierarchical data in C#

我正在尝试根据分层数据编写一个基本的 HTML 无序列表。数据如下所示:

    public class Task
    {
        public int Id { get; set; }
        public int? ParentId { get; set; }
        public string Title { get; set; }
        public Collection<Task> Tasks = new Collection<Task>( );
    }
    
    List<Task> tasks = new List<Task>( );
    
    tasks.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
    tasks.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
    tasks.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
    tasks.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
    tasks.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );

以下是我试图根据上述数据创建的内容:

<ul>
    <li>Top task
    <ul>
        <li>Subtask 1
            <ul>
                <li>Subtask 2</li>
            </ul>
        </li>
    </ul>
        <ul>
            <li>Subtask 3
                <ul>
                    <li>Subtask 4</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

我尝试了以下帖子并取得了一些成功:

Return unordered list from hierarchical sql dataC# Create HTML unordered list from List using Recursion

这是我从上面的第一个 link 改编而来的代码,它大部分都有效,但是如果更改了分层数据的顺序,该方法会在第一个不是 child 的前一个 parent。例如,如果我按如下方式更改数据列表的顺序,它将跳过子任务 1 和 2:

    tasks.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
    tasks.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
    tasks.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
    tasks.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
    tasks.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );
        public class Task
        {
            public int Id { get; set; }
            public int? ParentId { get; set; }
            public string Title { get; set; }
            public Collection<Task> Tasks = new Collection<Task>( );

            public Task FindTask( int id )
            {
                return FindTask( this, id );
            }

            private Task FindTask( Task task, int id )
            {
                if( task.Id == id )
                {
                    return task;
                }
                Task returnTask = null;
                foreach( Task child in task.Tasks )
                {
                    returnTask = child.FindTask( id );
                    if( returnTask != null )
                    {
                        break;
                    }
                }
                return returnTask;
            }
        }

        List<Task> topTasks = new List<Task>( );

        protected void GetHierarchy( int id )
        {
            List<Task> data = new List<Task>( );

            data.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
            data.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
            data.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
            data.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
            data.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );

            foreach( var row in data )
            {
                Task tasks = new Task( );
                tasks.Title = row.Title;
                tasks.Id = row.Id;
                if( row.ParentId == null )
                {
                    topTasks.Add( tasks );
                }
                else
                {
                    int parentId = row.ParentId.Value;
                    foreach( Task topTask in topTasks )
                    {
                        Task parentTask = topTask.FindTask( parentId );
                        if( parentTask != null )
                        {
                            parentTask.Tasks.Add( tasks );
                            break;
                        }
                    }
                }
            }
        }

        protected string Render( )
        {
            var s = new StringBuilder( );

            GetHierarchy( id );

            s.Append( "<ul>" );
            foreach( Task child in topTasks )
            {
                RenderTask( s, child );
            }
            s.Append( "</ul>" );

            return s.ToString( );
        }

        private void RenderTask( StringBuilder s, Task task )
        {
            s.Append( "<li>" );
            s.Append( task.Title );
            if( task.Tasks.Count > 0 )
            {
                s.Append( "<ul>" );
                foreach( Task child in task.Tasks )
                {
                    RenderTask( s, child );
                }
                s.Append( "</ul>" );
            }
            s.Append( "</li>" );
        }

我不想使用 jquery 树视图或任何其他组件,因为这需要一个独立的解决方案。

任何人都可以建议我更好的方法,或者至少帮助我修复我在上面实现的代码,以便数据的顺序不会破坏递归?

提前致谢。

在您的 GetHierarchy 函数中,您将按顺序循环访问列表中的每个任务。如果列表中的第一个任务的 parentId 为空,那么它将被添加到 topTasks 列表中。

但是,如果第一个任务有一个非空的 parentId,那么它将被添加到 topTasks 中的父任务。请注意,这里的父任务还不存在于 topTasks 列表中,因此不会插入该任务。

我会使用一个递归函数,它首先获取具有空父项的任务,然后使用该任务的 ID 递归调用该函数,直到它到达树的底部。

一个粗略的例子:

Task addSubLists (List<Task> data, Task parent) {
    foreach(Task task in data) {
        if (task.parentId.Value == parent.Id.Value) {
            parent.Tasks.Add(addSubLists(data, task));
        }
    }
    return parent;
}

您将顶级任务传递给函数,它会添加所有后代。

最后,我改编了C# Create HTML unordered list from List using Recursion的答案,效果很好。这是我使用的代码:

        protected string WriteList( )
        {
            var s = new StringBuilder( );
            List<Task> tasks = new List<Task>( );

            tasks.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
            tasks.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
            tasks.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
            tasks.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
            tasks.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );


            s.Append( "<ul>" );
            foreach( var task in tasks )
            {
                if( task.ParentId == null )
                {
                    WriteTaskList( tasks, task, s );
                }
            }
            s.Append( "</ul>" );

            return s.ToString( );
        }

        private static void WriteTaskList( List<Task> tasks, Task task, StringBuilder s )
        {
            s.Append( "<li>" + task.Title );

            var subtasks = tasks.Where( p => p.ParentId == task.Id );

            if( subtasks.Count( ) > 0 )
            {
                s.Append( "<ul>" );
                foreach( Task p in subtasks )
                {
                    if( tasks.Count( x => x.ParentId == p.Id ) > 0 )
                        WriteTaskList( tasks, p, s );
                    else
                        s.Append( string.Format( "<li>{0}</li>", p.Title ) );
                }
                s.Append( "</ul>" );
            }

            s.Append( "</li>" );
        }