您不需要 HtmlHelper,您实际需要的只是一种将DropdownTreeview
对象图转换为IEnumerable<SelectListItem>
可以传递到现有DropDownFor
HtmlHelper 以及(可以说是优越的)SelectTagHelper
(aka <select asp-items>
)的方法。
因为您的class DropdownTreeview
项目不包含对父项目的任何对象引用class DropdownTreeview
(而不是您正在使用long? IdParent
),所以您不能使用现有的直接方法来遍历图形。在我的方法中,我将这些转换为可遍历的对象图,然后可以将其展平为SelectListItem
对象列表。
我要将您重命名class DropdownTreeview
为,class TreeNode
因为每个实例代表一个节点,而不是整个树。
您可以直接将其复制并粘贴到 Linqpad 中以查看它的运行情况(当然,您需要添加对 ASP.NET Core 的 NuGet 引用才能使用SelectListItem
):
void Main()
{
TreeNode[] initialNodes = new[]
{
new TreeNode( 1, null, "Computers" ),
new TreeNode( 2, 1, "Desktops" ),
new TreeNode( 3, 1, "Notebooks" ),
new TreeNode( 4, 1, "Software" ),
new TreeNode( 5, null, "Electronics" ),
new TreeNode( 6, 5, "Camera photo" ),
new TreeNode( 7, 5, "Cell phones" ),
new TreeNode( 8, 5, "Others" ),
new TreeNode( 9, null, "Apparel" ),
new TreeNode( 10, 9, "Shoes" ),
new TreeNode( 11, 9, "Clothing" ),
new TreeNode( 12, 11, "Shirt" ),
new TreeNode( 13, 11, "TShirt" ),
new TreeNode( 14, 9, "Accessories" ),
new TreeNode( 15, null, "Downloads" ),
new TreeNode( 16, null, "Books" ),
new TreeNode( 17, null, "Jewelry" ),
new TreeNode( 18, null, "Gift Cards" )
};
TreeGraph graph = new TreeGraph( initialNodes );
// graph.EnumerateDepthFirst().Dump();
graph.AsSelectListItems().Dump();
}
public record TreeNode( Int64 id, Int64? parentId, String name );
public class TreeGraph
{
private readonly IReadOnlyDictionary<Int64,( TreeNode node, List<TreeNode> children )> nodesById;
public TreeGraph( IEnumerable<TreeNode> initialNodes )
{
Dictionary<Int64,( TreeNode node, List<TreeNode> children )> nodesByIdMut = initialNodes
.ToDictionary( n => n.id, n => ( node: n, children: new List<TreeNode>() ) );
foreach( TreeNode child in initialNodes.Where( n => n.parentId.HasValue ) )
{
nodesByIdMut[ child.parentId!.Value ].children.Add( child );
}
this.nodesById = nodesByIdMut;
}
private String GetPath( TreeNode node )
{
if( node.parentId == null ) return node.name;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode? n = node;
while( n != null )
{
stack.Push( n );
n = n.parentId.HasValue ? ( nodesById[ n.parentId.Value ].node ) : null;
}
return String.Join( separator: " > ", stack.Select( n2 => n2.name ) );
}
private IEnumerable<TreeNode> GetRoots()
{
return this.nodesById.Values
.Where( t => t.node.parentId == null )
.OrderBy( t => t.node.name )
.Select( t => t.node );
}
private IEnumerable<TreeNode> GetChildren( TreeNode node )
{
return this.nodesById[ node.id ].children;
}
public IEnumerable<( TreeNode n, String path )> EnumerateDepthFirst()
{
foreach( TreeNode root in this.GetRoots() )
{
foreach( ( TreeNode descendant, String path ) pair in this.EnumerateDepthFirstFrom( root ) )
{
yield return pair;
}
}
}
private IEnumerable<( TreeNode n, String path )> EnumerateDepthFirstFrom( TreeNode root )
{
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.Push( root );
while( stack.Count > 0 )
{
TreeNode n = stack.Pop();
foreach( TreeNode c in this.GetChildren( n ) )
{
stack.Push( c );
}
String path = this.GetPath( n );
yield return ( n, path );
}
}
public IEnumerable<SelectListItem> AsSelectListItems()
{
return this.EnumerateDepthFirst()
.Select( pair => new SelectListItem()
{
Text = pair.path,
Value = pair.n.id.ToString( CultureInfo.InvariantCulture )
} )
}
}
截图证明:
然后在 Razor ( .cshtml
) 视图中使用它,实例化TreeGraph
某处并将结果传递AsSelectListItems()
到 into asp-items
,如下所示:
@model MyPageViewModel
TreeNode[] nodes = this.Model.TreeNodes;
TreeGraph graph = new TreeGraph( nodes );
//
<form>
<select asp-items="@( graph.AsSelectListItems() )"></select>
</form>