您不需要 HtmlHelper,您真正需要的只是一种将 DropdownTreeview 对象图转换为 IEnumerable<SelectListItem> 的方法,您可以将其传递到现有的 DropDownFor HtmlHelper 以及 (可以说是优越)SelectTagHelper(又名<select asp-items>)。
因为您的 class DropdownTreeview 项目不包含任何 object-references 到父 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() 的结果传递给 asp-items,如下所示:
@model MyPageViewModel
TreeNode[] nodes = this.Model.TreeNodes;
TreeGraph graph = new TreeGraph( nodes );
//
<form>
<select asp-items="@( graph.AsSelectListItems() )"></select>
</form>