如何绑定MudBlazor TreeView到自定义对象及实现子节点懒加载
MudBlazor动态TreeView实现方案
1. 绑定对象构造要求
MudTreeView没有强制绑定特定类型,你可以自定义和数据库表结构匹配的节点实体类,只要满足两个核心要求即可:
- 包含唯一标识、父节点标识两个字段,用于层级关联
- 包含子节点集合属性,类型可以用
List<T>或者IEnumerable<T>,T为你自定义的节点类
推荐的自定义节点类示例:
public class TreeNodeDto { // 节点唯一ID,和数据库主键对应 public int Id { get; set; } // 父节点ID,一级节点的ParentId统一设为null public int? ParentId { get; set; } // 节点显示文本 public string Name { get; set; } // 节点展开状态,用于和MudTreeView绑定 public bool IsExpanded { get; set; } // 子节点集合 public List<TreeNodeDto> Children { get; set; } = new(); // 标记子节点是否已加载,避免重复请求数据库 public bool IsChildrenLoaded { get; set; } }
如果不想自定义类,也可以直接使用MudBlazor自带的TreeViewItemData<T>类,自定义类的优势是更贴合业务场景,扩展属性更方便。
2. 一级节点初始化+点击懒加载子节点逻辑
初始加载逻辑
页面初始化时,只从数据库查询所有ParentId == null的一级节点,赋值给MudTreeView绑定的根节点集合即可,不需要一次性拉取全量数据。
点击加载子节点逻辑
通过MudTreeView的ExpandedChanged事件触发子节点加载:
- 节点被展开时先判断
IsChildrenLoaded状态,如果已经加载过直接返回 - 如果未加载,将当前节点ID作为
ParentId查询条件拉取子节点,赋值到当前节点的Children属性,同时将IsChildrenLoaded设为true即可
3. 完整实现示例
页面代码
@page "/dynamic-treeview" @inject ITreeRepository TreeRepository <!-- 替换为你自己的数据库访问服务 --> <MudTreeView Items="RootNodes" T="TreeNodeDto" ExpandedChanged="OnNodeExpanded"> <ItemTemplate> <MudTreeViewItem @bind-Expanded="context.IsExpanded" T="TreeNodeDto" Value="@context"> <TitleContent> @context.Name <!-- 新增、删除操作按钮 --> <MudIconButton Icon="@Icons.Material.Filled.Add" Size="Size.Small" OnClick="() => AddNode(context)" Class="ml-2" /> <MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" OnClick="() => DeleteNode(context)" Class="ml-1" /> </TitleContent> <Items> @context.Children </Items> </MudTreeViewItem> </ItemTemplate> </MudTreeView> @code { // 根节点集合,初始仅存储一级节点 private List<TreeNodeDto> RootNodes { get; set; } = new(); protected override async Task OnInitializedAsync() { // 初始加载所有一级节点 RootNodes = await TreeRepository.GetListByParentIdAsync(null); } private async Task OnNodeExpanded(TreeNodeDto node, bool isExpanded) { // 仅在展开且子节点未加载时请求数据库 if (isExpanded && !node.IsChildrenLoaded) { node.Children = await TreeRepository.GetListByParentIdAsync(node.Id); node.IsChildrenLoaded = true; } } // 新增节点逻辑 private async Task AddNode(TreeNodeDto parentNode) { // 实际场景可替换为弹窗输入节点信息 var newNode = new TreeNodeDto { Name = "新节点", ParentId = parentNode.Id }; // 写入数据库 var savedNode = await TreeRepository.AddAsync(newNode); // 更新内存集合触发UI刷新 parentNode.Children.Add(savedNode); } // 删除节点逻辑 private async Task DeleteNode(TreeNodeDto node) { // 数据库删除 await TreeRepository.DeleteAsync(node.Id); // 从内存集合中移除对应节点 if (node.ParentId == null) { RootNodes.Remove(node); return; } var parentNode = FindParentNode(RootNodes, node.ParentId.Value); parentNode?.Children.Remove(node); } // 辅助方法:递归查找父节点 private TreeNodeDto FindParentNode(List<TreeNodeDto> nodes, int parentId) { foreach (var node in nodes) { if (node.Id == parentId) return node; var childResult = FindParentNode(node.Children, parentId); if (childResult != null) return childResult; } return null; } }
注意事项
- 数据库访问逻辑可以根据你使用的框架替换为EF Core、SqlSugar等对应的查询代码,示例中的
ITreeRepository仅为抽象演示 - 新增/删除操作完成后直接操作对应内存集合即可,Blazor会自动触发UI重绘,无需额外调用刷新方法
- 如果需要支持节点选中、禁用等能力,直接在
TreeNodeDto中添加对应属性,绑定到MudTreeViewItem的对应参数即可
内容的提问来源于stack exchange,提问作者user976508
相关产品推荐
相关产品推荐

