Tree View

An extensively customizable tree view component with selection and lazy-loading support.

Basic

The TreeView allows exploring of hierarchic data. In its simplest form it only displays text via the Text property of its items. But you can also attach a value of type T to each item via the Value property. In this example ReadOnly is set to true to prevent value selection. Some items in this example have a text, some have a value and one has both. If you use only Text and T="string" then that text will also serve as value. If you set only Value then the text will be derived from the value. You can of course set both to have different Text and Value. This will become important for value selection (see Selection).

  • Getting Started

    • Installation

  • Components

    • Avatar

    • Button

<MudTreeView T="string" ReadOnly>
    <MudTreeViewItem Text="Getting Started">
        <MudTreeViewItem Text="Installation" />
    </MudTreeViewItem>
    <MudTreeViewItem Value='"Components"'>
        <MudTreeViewItem Text="Avatar" Value='"MudAvatar"' />
        <MudTreeViewItem Text="Button" Value='"MudButton"' />
    </MudTreeViewItem>
</MudTreeView>
Usage

Hover applies a hover effect on mouse-over. Ripple applies a ripple effect on click, except if ExpandOnDoubleClick is set. Dense will result in a more compact vertical padding of the item items to save space. Disabled will prevent all interaction with any items. With ExpandOnClick a subtree can be expanded and collapsed by clicking on it. With ExpandOnDoubleClick, only a double-click will expand or collapse the subtrees. Additionally, a OnDoubleClick callback can be assigned to set a custom double click behaviour.

Note that ExpandOnDoubleClick overrules ExpandOnClick if both are set.

  • Applications

    • Terminal

  • Documents

    • MudBlazor

      • API

      • Components

      • Features

<MudPaper Width="300px" Elevation="0">
    <MudTreeView T="string" ReadOnly="@ReadOnly" Hover="@Hover" Dense="@Dense" Disabled="@Disabled"
                 ExpandOnClick="@ExpandOnClick" ExpandOnDoubleClick="@ExpandOnDoubleClick">
        <MudTreeViewItem Text="Applications" Expanded>
            <MudTreeViewItem Text="Terminal" />
        </MudTreeViewItem>
        <MudTreeViewItem Text="Documents" Expanded>
            <MudTreeViewItem Text="MudBlazor" Expanded>
                <MudTreeViewItem Text="API" />
                <MudTreeViewItem Text="Components" />
                <MudTreeViewItem Text="Features" />
            </MudTreeViewItem>
        </MudTreeViewItem>
    </MudTreeView>
</MudPaper>

<MudStack Row Wrap="Wrap.Wrap" Justify="Justify.Center">
    <MudSwitch @bind-Value="ReadOnly" Color="Color.Primary">ReadOnly</MudSwitch>
    <MudSwitch @bind-Value="Hover" Color="Color.Primary">Hover</MudSwitch>
    <MudSwitch @bind-Value="Ripple" Color="Color.Primary">Ripple</MudSwitch>
    <MudSwitch @bind-Value="Dense" Color="Color.Primary">Dense</MudSwitch>
    <MudSwitch @bind-Value="Disabled" Color="Color.Primary">Disabled</MudSwitch>
    <MudSwitch @bind-Value="ExpandOnClick" Color="Color.Primary">ExpandOnClick</MudSwitch>
    <MudSwitch @bind-Value="ExpandOnDoubleClick" Color="Color.Primary">ExpandOnDoubleClick</MudSwitch>
</MudStack>
@code {
    public bool ReadOnly = true;
    public bool Hover = true;
    public bool Ripple;
    public bool Dense;
    public bool Disabled;
    public bool ExpandOnClick = true;
    public bool ExpandOnDoubleClick;
}
Icons

The icons and their color can be changed individually per item via Icon and IconColor. This example uses a custom ExpandButtonIcon and shows how to apply an alternative icon for expanded subtrees via the IconExpanded property.

  • All Mail

  • Drafts

  • Orders

  • Categories

    • Social

    • Updates

    • Forums

    • Spam

  • Trash

<MudTreeView T="string">
    <MudTreeViewItem Value='"All Mail"' Icon="@Icons.Material.Filled.Email" />
    <MudTreeViewItem Value='"Drafts"' Icon="@Icons.Material.Filled.Drafts" />
    <MudTreeViewItem Value='"Orders"' Icon="@Icons.Material.Filled.Label" IconColor="Color.Secondary" />
    <MudTreeViewItem Value='"Categories"' Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen" IconColor="Color.Info"
                     ExpandButtonIcon="@Icons.Material.Filled.ArrowRight" ExpandButtonIconColor="Color.Primary">
        <MudTreeViewItem Value='"Social"' Icon="@Icons.Material.Filled.Group" />
        <MudTreeViewItem Value='"Updates"' Icon="@Icons.Material.Filled.Info" IconColor="Color.Tertiary" />
        <MudTreeViewItem Value='"Forums"' Icon="@Icons.Material.Filled.QuestionAnswer" />
        <MudTreeViewItem Value='"Spam"' Icon="@Icons.Material.Filled.LocalOffer" />
    </MudTreeViewItem>
    <MudTreeViewItem Value='"Trash"' Icon="@Icons.Material.Filled.Delete" />
</MudTreeView>
Single Selection

If you set SelectionMode to SelectionMode.SingleSelection you can select a single value from the entire tree. SelectionMode.ToggleSelection is similar, except that it allows to deselect a previously selected value by clicking on it again. You can use @bind-SelectedValue on the <MudTreeView> to get updates about the selected value or to influence the selected value like you can do in this example with the chip set. The color of the selected item can be changed with Color property.

  • config

    • launch.json

    • tasks.json

  • images

    • logo.png

config
launch.json
tasks.json
images
logo.png
<MudPaper Width="300px" Elevation="0">
    <MudTreeView Hover ReadOnly="@ReadOnly" @bind-SelectedValue="SelectedValue" SelectionMode="@SelectionMode">
        <MudTreeViewItem Value="@("config")" Expanded Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
            <MudTreeViewItem Value='"launch.json"' Icon="@Icons.Custom.FileFormats.FileCode" />
            <MudTreeViewItem Value='"tasks.json"' Icon="@Icons.Custom.FileFormats.FileCode" />
        </MudTreeViewItem>
        <MudTreeViewItem Value="@("images")" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
            <MudTreeViewItem Value="@("logo.png")" Icon="@Icons.Custom.FileFormats.FileImage" />
        </MudTreeViewItem>
    </MudTreeView>
</MudPaper>

<MudStack Row Justify="Justify.Center" Style="width: 100%" Wrap="Wrap.Wrap">
    <MudRadioGroup @bind-Value="SelectionMode">
        <MudRadio Value="SelectionMode.SingleSelection" Color="Color.Primary">SingleSelection</MudRadio>
        <MudRadio Value="SelectionMode.ToggleSelection" Color="Color.Primary">ToggleSelection</MudRadio>
    </MudRadioGroup>
    <MudChipSet T="string" @bind-SelectedValue="SelectedValue" Color="Color.Primary" Variant="Variant.Text">
        <MudChip Text="config"/>
        <MudChip Text="launch.json"/>
        <MudChip Text="tasks.json"/>
        <MudChip Text="images"/>
        <MudChip Text="logo.png"/>
    </MudChipSet>
    <MudSwitch @bind-Value="ReadOnly" Color="Color.Primary">ReadOnly</MudSwitch>    
</MudStack>
@code {
    public string SelectedValue = "tasks.json";
    public bool ReadOnly = false;
    public SelectionMode SelectionMode = SelectionMode.SingleSelection;
}
Multi Selection

If you set SelectionMode to SelectionMode.MultiSelection you can select multiple values from the entire tree. Use @bind-SelectedValues (note the 's' at the end) on the <MudTreeView> to get updates about the selected values or to influence the selection like you can do in this example with the chip set. The color of the checkboxes can be changed with the CheckBoxColor property.

  • bundle.zip

    • config

      • launch.json

      • tasks.json

    • images

      • logo.png

bundle.zip
config
launch.json
tasks.json
images
logo.png
<MudPaper Width="300px" Elevation="0">
    <MudTreeView Hover ReadOnly="@ReadOnly" TriState="@TriState" AutoSelectParent="@AutoSelectParent" @bind-SelectedValues="SelectedValues" SelectionMode="SelectionMode.MultiSelection"
                 CheckBoxColor="Color.Info">
        <MudTreeViewItem Text="bundle.zip" Expanded Icon="@Icons.Material.Filled.FolderZip">
            <MudTreeViewItem Text="config" Expanded Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
                <MudTreeViewItem Value='"launch.json"' Icon="@Icons.Custom.FileFormats.FileCode" />
                <MudTreeViewItem Value='"tasks.json"' Icon="@Icons.Custom.FileFormats.FileCode" />
            </MudTreeViewItem>
            <MudTreeViewItem Text="images" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
                <MudTreeViewItem Value="@("logo.png")" Icon="@Icons.Custom.FileFormats.FileImage" />
            </MudTreeViewItem>
        </MudTreeViewItem>
    </MudTreeView>
</MudPaper>

<MudStack Row Justify="Justify.Center" Style="width: 100%" Wrap="Wrap.Wrap">
    <MudChipSet T="string" @bind-SelectedValues="SelectedValues" SelectionMode="SelectionMode.MultiSelection" Color="Color.Info" Variant="Variant.Text">
        <MudChip Text="bundle.zip"/>
        <MudChip Text="config"/>
        <MudChip Text="launch.json"/>
        <MudChip Text="tasks.json"/>
        <MudChip Text="images"/>
        <MudChip Text="logo.png"/>
    </MudChipSet>
    <MudSwitch @bind-Value="TriState" Color="Color.Info">TriState</MudSwitch>
    <MudSwitch @bind-Value="AutoSelectParent" Color="Color.Info">AutoSelectParent</MudSwitch>
    <MudSwitch @bind-Value="ReadOnly" Color="Color.Info">ReadOnly</MudSwitch>    
</MudStack>
@code {
    public IReadOnlyCollection<string> SelectedValues = ["tasks.json", "launch.json"];
    public bool ReadOnly = false;
    public bool TriState = true;
    public bool AutoSelectParent = true;
}
Binding Items Directly

You can also bind the parameters Selected and Expanded on individual <MudTreeViewItems>. Of course, manipulating the selection should typically happen via binding SelectedValue or SelectedValues on the <MudTreeView> but binding the item's parameters makes sense when using the item template (see next example).

  • config

    • launch.json

    • tasks.json

  • images

    • logo.png

<MudPaper Width="300px" Elevation="0">
    <MudTreeView T="string" Hover SelectionMode="@SelectionMode.ToggleSelection" Color="Color.Tertiary">
        <MudTreeViewItem Text="config" @bind-Expanded="ConfigExpanded" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
            <MudTreeViewItem Text="launch.json" @bind-Selected="LaunchSelected"  Icon="@Icons.Custom.FileFormats.FileCode" />
            <MudTreeViewItem Text="tasks.json" @bind-Selected="TasksSelected" Icon="@Icons.Custom.FileFormats.FileCode" />
        </MudTreeViewItem>
        <MudTreeViewItem Text="images" @bind-Expanded="ImagesExpanded" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
            <MudTreeViewItem Text="logo.png" @bind-Selected="LogoSelected" Icon="@Icons.Custom.FileFormats.FileImage" />
        </MudTreeViewItem>
    </MudTreeView>
</MudPaper>

<MudStack Row Justify="Justify.Center" AlignItems="AlignItems.Center" Style="width: 100%" Wrap="Wrap.Wrap">
    <MudSwitch @bind-Value="ConfigExpanded" Color="Color.Tertiary">config</MudSwitch>
    <MudSwitch @bind-Value="ImagesExpanded" Color="Color.Tertiary">images</MudSwitch>
    <MudCheckBox @bind-Value="LaunchSelected" Color="Color.Tertiary">launch.json</MudCheckBox>
    <MudCheckBox @bind-Value="TasksSelected" Color="Color.Tertiary">tasks.json</MudCheckBox>
    <MudCheckBox @bind-Value="LogoSelected" Color="Color.Tertiary">logo.png</MudCheckBox>
</MudStack>
@code {
    public bool ConfigExpanded;
    public bool ImagesExpanded;
    public bool LaunchSelected = true;
    public bool TasksSelected;
    public bool LogoSelected;
}
Auto-Expand

With AutoExpand set to true collapsed sub-trees that become selected will be expanded automatically. To test it, select chips and see how the corresponding tree items will be expanded. To expand or collapse all levels of the tree use the public members ExpandAll() and CollapseAll().

  • C:

    • config

      • launch.json

      • tasks.json

    • images

      • logo.png

config
launch.json
tasks.json
images
logo.png
<MudPaper Width="300px" Elevation="0">
    <MudTreeView @ref="TreeView" Hover AutoExpand="@AutoExpand" @bind-SelectedValue="SelectedValue" SelectionMode="@SelectionMode.ToggleSelection">
        <MudTreeViewItem Value="@("C:")" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
            <MudTreeViewItem Value="@("config")" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
                <MudTreeViewItem Value='"launch.json"' Icon="@Icons.Custom.FileFormats.FileCode" />
                <MudTreeViewItem Value='"tasks.json"' Icon="@Icons.Custom.FileFormats.FileCode" />
            </MudTreeViewItem>
            <MudTreeViewItem Value="@("images")" Icon="@Icons.Custom.Uncategorized.Folder" IconExpanded="@Icons.Custom.Uncategorized.FolderOpen">
                <MudTreeViewItem Value="@("logo.png")" Icon="@Icons.Custom.FileFormats.FileImage" />
            </MudTreeViewItem>
        </MudTreeViewItem>
    </MudTreeView>
</MudPaper>

<MudStack Row Justify="Justify.Center" Style="width: 100%" Wrap="Wrap.Wrap">
    <MudButton Color="Color.Primary" OnClick="@(()=>TreeView.ExpandAllAsync())" Variant="Variant.Filled">Expand All</MudButton>
    <MudButton Color="Color.Dark" OnClick="@(()=>TreeView.CollapseAllAsync())" Variant="Variant.Filled">Collapse All</MudButton>
    <MudChipSet T="string" @bind-SelectedValue="SelectedValue" Color="Color.Primary" Variant="Variant.Text" SelectionMode="@SelectionMode.ToggleSelection">
        <MudChip Text="config"/>
        <MudChip Text="launch.json"/>
        <MudChip Text="tasks.json"/>
        <MudChip Text="images"/>
        <MudChip Text="logo.png"/>
    </MudChipSet>
    <MudSwitch @bind-Value="AutoExpand" Color="Color.Primary">AutoExpand</MudSwitch>
</MudStack>
@code {
    public MudTreeView<string> TreeView;
    public string SelectedValue = null;
    public bool AutoExpand = true;
}
Item Template

This example shows how to use ItemTemplate to automatically build the tree items according to a hierarchical data structure.

  • All Mail

  • Trash

  • Categories

    • Social

      90
    • Updates

      2294
    • Forums

      3566
    • Promotions

      733
  • History

Sum of selected items:

0

<MudPaper Width="300px" Elevation="0">
    <MudTreeView Items="@TreeItems" SelectionMode="SelectionMode.MultiSelection" @bind-SelectedValues="SelectedValues">
        <ItemTemplate>
            @{
                // Casting context from TreeItemData<string> to our own derived class TreeItemPresenter
                // for convenient usage in the template
                var presenter = context as TreeItemPresenter;
            }
            <MudTreeViewItem @bind-Expanded="@context.Expanded" Items="@context.Children" Value="@context.Value"
                             Icon="@context.Icon" Text="@context.Text" EndText="@presenter?.Number?.ToString()" EndTextTypo="@Typo.caption" />
        </ItemTemplate>
    </MudTreeView>
</MudPaper>

<MudStack Row Justify="Justify.Center" Style="width: 100%">
    <MudText Typo="@Typo.subtitle1">Sum of selected items: <MudChip T="string">@GetSelectedSum()</MudChip></MudText>
</MudStack>
@code {
    public IReadOnlyCollection<string> SelectedValues { get; set; }

    public List<TreeItemData<string>> TreeItems { get; set; } = new();
    public Dictionary<string, int?> ValueMap { get; set; }

    public class TreeItemPresenter : TreeItemData<string>
    {
        public int? Number { get; set; }

        public TreeItemPresenter(string text, string icon, int? number = null) : base(text)
        {
            Text = text;
            Icon = icon;
            Number = number;
        }
    }

    protected override void OnInitialized()
    {
        TreeItems.Add(new TreeItemPresenter("All Mail", Icons.Material.Filled.Email));
        TreeItems.Add(new TreeItemPresenter("Trash", Icons.Material.Filled.Delete));
        TreeItems.Add(new TreeItemPresenter("Categories", Icons.Material.Filled.Label) {
            Expanded = true,
            Children = [
                    new TreeItemPresenter("Social", Icons.Material.Filled.Group, 90),
                    new TreeItemPresenter("Updates", Icons.Material.Filled.Info, 2294),
                    new TreeItemPresenter("Forums", Icons.Material.Filled.QuestionAnswer, 3566),
                    new TreeItemPresenter("Promotions", Icons.Material.Filled.LocalOffer, 733)
                ]
        });
        TreeItems.Add(new TreeItemPresenter("History", Icons.Material.Filled.Label));
        ValueMap = TreeItems.Concat(TreeItems.SelectMany(x => x.Children ?? [])).OfType<TreeItemPresenter>().ToDictionary(x => x.Value, x => x.Number);
    }

    public int GetSelectedSum()
    {
        if (SelectedValues is null)
            return 0;
        return SelectedValues.Select(x => ValueMap.GetValueOrDefault(x, 0)).Sum(i => i ?? 0);
    }
}
Server Side Data

Data can be loaded on demand with the use of the ServerData parameter, the loading icon and its color can be changed with LoadingIcon and LoadingIconColor prop. Lazy-loading can also be disabled for certain items with the CanExpand property, if you know that there are is no subtree.

  • All Mail

    • Trash

      @using System.Collections.ObjectModel
      
      <MudPaper Width="300px" Elevation="0">
          <MudTreeView ServerData="@LoadServerData" Items="@InitialTreeItems">
              <ItemTemplate>
                  <MudTreeViewItem Value="@context.Value" Icon="@context.Icon" LoadingIconColor="Color.Info" CanExpand="@context.Expandable"/>
              </ItemTemplate>
          </MudTreeView>
      </MudPaper>
      
      @code {
          private List<TreeItemData<string>> InitialTreeItems { get; set; } = new();
          private List<TreeItemData<string>> ServerTreeItems { get; set; } = new();
      
          protected override void OnInitialized()
          {
              // MudTreeView initially only gets these top-level items
              InitialTreeItems.Add(new TreeItemData<string> { Value = "All Mail", Icon = Icons.Material.Filled.Label, });
              InitialTreeItems.Add(new TreeItemData<string> { Value = "Trash", Icon = Icons.Material.Filled.Delete });
      
              // LoadServerData will load from this hierarchy
              ServerTreeItems.Add(new TreeItemData<string> {
                      Value = "All Mail", Icon = Icons.Material.Filled.Label,
                      Children = [
                              new TreeItemData<string> { Value = "Promotions", Icon = Icons.Material.Filled.Group, 
                                      Children = [
                                          new TreeItemData<string> { Value = "L.E.D Door Mats", Icon = Icons.Material.Outlined.Lightbulb, Expandable = false },
                                          new TreeItemData<string> { Value = "Car Beauty Salon", Icon = Icons.Material.Filled.CarRepair, Expandable = false },
                                          new TreeItemData<string> { Value = "Fakedoors.com", Icon = Icons.Material.Outlined.DoorFront, Expandable = false },
                                          new TreeItemData<string> { Value = "Bluetooth Toilet", Icon = Icons.Material.Filled.Wc, Expandable = false }
                                      ]},
                          new TreeItemData<string> { Value = "Updates", Icon = Icons.Material.Filled.Info, Expandable = false },
                          new TreeItemData<string> { Value = "Forums", Icon = Icons.Material.Filled.QuestionAnswer, Expandable = false },
                          new TreeItemData<string> { Value = "Social", Icon = Icons.Material.Filled.LocalOffer, Expandable = false }
                      ]});
          }
      
          public async Task<IReadOnlyCollection<TreeItemData<string>>> LoadServerData(string parentValue)
          {
              // wait 500ms to simulate a server load, then recursively search through our tree to find the child items for the given value
              await Task.Delay(500);
              foreach (var item in ServerTreeItems) {
                  if (item.Value == parentValue)
                      return item.Children;
                  if (!item.HasChildren)
                      continue;
                  var descendentItem = FindTreeItemData(parentValue, item);
                  if (descendentItem != null)
                      return descendentItem.Children;
              }
              return null;
          }
      
          private TreeItemData<string> FindTreeItemData(string value, TreeItemData<string> parent)
          {
              foreach (var child in parent.Children) {
                  if (child.Value == value)
                      return child;
                  if (!child.HasChildren)
                      continue;
                  var descendentItem = FindTreeItemData(value, child);
                  if (descendentItem != null)
                      return descendentItem;
              }
              return null;
          }
      
      }
      
      Custom Look and Behavior

      When the Content property is used, it will completely replace the default rendering of the MudTreeViewItem to use your own. This gives you every opportunity to change the behavior of MudTreeView to anything you want. In this example we build our own non-standard multi selection behavior where selecting the parent node does not automatically select the children and vice-versa. Also, note how only certain items can be selected.

      By the way, to get nice scrolling behavior like in this example, you must constrain the container's height or max-height and set the container's overflow-y accordingly.

      • Coffee Makers

        • Moka Pot

          $36.99
        • French Press

          $19.99
          • Spare Sieve

            $6.00
          • Cleaning Kit

            $17.59
      • Tea Pots

        • Glass Teapot

          $36.99
          • Glass Infuser

            $2.99
          • Stainless Steel Infuser

            $5.99
        • Stainless Steel Teapot

          $14.15
        • Japanese Cast Iron Teapot

          $26.39
        • Porcelain Teapot

          $38.00

      Selected items:

      Sum:

      $0.00

      <MudStack>
          <MudPaper Width="400px" MaxHeight="400px" Class="overflow-y-auto" Elevation="2">
              <MudTreeView Items="@TreeItems" ReadOnly>
                  <ItemTemplate>
                      @{                
                          var product = context.Value; // for convenient usage in the template
                      }
                      <MudTreeViewItem @bind-Expanded="@context.Expanded" Items="@context.Children" Value="@context.Value">
                          <Content>
                              <MudTreeViewItemToggleButton @bind-Expanded="@context.Expanded" Visible="@context.HasChildren" />
                              @if (product?.Price is not null) {
                                  <MudCheckBox T="bool" Size="Size.Small" Value="@context.Selected" ValueChanged="@((v)=> OnCheckboxChanged(v, context))" />
                              }
                              <MudIcon Icon="@product?.Icon" Class="ml-0 mr-2" Color="@Color.Default" />
                              <MudText>@product?.Name</MudText>
                              @if (product?.Price is not null) {
                                  <MudChip Class="ml-1">@product.Price.Value.ToString("C")</MudChip>
                              }
                          </Content>
                      </MudTreeViewItem>
                  </ItemTemplate>
              </MudTreeView>
          </MudPaper>
          <MudStack Class="mt-3" Style="width: 400px">
              <MudText Typo="@Typo.subtitle1" Inline>Selected items: @(string.Join(", ", (SelectedValues ?? []).Select(x => x.Name)))</MudText>
              <MudStack>
                  <MudText Typo="@Typo.subtitle1">Sum: <MudChip T="string">@GetSelectedSum().ToString("C")</MudChip></MudText>
              </MudStack>
          </MudStack>
      </MudStack>
      
      @code {
          public void OnCheckboxChanged(bool selected, TreeItemData<Product> context)
          {
              context.Selected = selected;
              if (context.Value?.Price is null)
                  return;
              if (context.Selected)
                  SelectedValues.Add(context.Value);
              else
                  SelectedValues.Remove(context.Value);
          }
      
          public HashSet<Product> SelectedValues { get; set; } = new();
      
          public List<TreeItemData<Product>> TreeItems { get; set; } = new();
      
          public class Product : IEquatable<Product>
          {
              public decimal? Price { get; set; }
              public string Name { get; init; }
              public string Icon { get; set; }
      
              public Product(string name, string icon, decimal? price = null)
              {
                  Name = name;
                  Icon = icon;
                  Price = price;
              }
      
              public bool Equals(Product other)
              {
                  if (ReferenceEquals(null, other)) return false;
                  if (ReferenceEquals(this, other)) return true;
                  return Name == other.Name;
              }
      
              public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is Product other && Equals(other);
      
              public override int GetHashCode() => Name?.GetHashCode() ?? 0;
          }
      
          protected override void OnInitialized()
          {
              TreeItems.Add(new TreeItemData<Product> { 
                  Value=new Product("Coffee Makers", Icons.Material.Filled.Label), 
                  Expanded = true,
                  Children = [
                      new TreeItemData<Product> { Value=new Product("Moka Pot", Icons.Material.Filled.LocalOffer, 36.99m),  },
                      new TreeItemData<Product> { 
                          Value=new Product("French Press", Icons.Material.Filled.LocalOffer, 19.99m),
                          Expanded = true,
                          Children = [ 
                              new TreeItemData<Product> { Value = new Product("Spare Sieve", Icons.Material.Filled.LocalOffer, 6.00m) },
                              new TreeItemData<Product> { Value = new Product("Cleaning Kit", Icons.Material.Filled.LocalOffer, 17.59m) }
                          ]
                      }
                  ]
              });
              TreeItems.Add(new TreeItemData<Product> {
                  Value = new Product("Tea Pots", Icons.Material.Filled.Label),
                  Expanded = true,
                  Children = [
                      new TreeItemData<Product> {
                          Value=new Product("Glass Teapot", Icons.Material.Filled.LocalOffer, 36.99m),
                          Expanded = true,
                          Children = [ 
                              new TreeItemData<Product> { Value = new Product("Glass Infuser", Icons.Material.Filled.LocalOffer, 2.99m) }, 
                              new TreeItemData<Product> { Value = new Product("Stainless Steel Infuser", Icons.Material.Filled.LocalOffer, 5.99m) }
                          ]
                      },
                      new TreeItemData<Product> { Value = new Product("Stainless Steel Teapot", Icons.Material.Filled.LocalOffer, 14.15m) },
                      new TreeItemData<Product> { Value = new Product("Japanese Cast Iron Teapot", Icons.Material.Filled.LocalOffer, 26.39m) },
                      new TreeItemData<Product> { Value = new Product("Porcelain Teapot", Icons.Material.Filled.LocalOffer, 38.00m) }
                  ]
              });
          }
      
          public decimal GetSelectedSum()
          {
              if (SelectedValues is null)
                  return 0;
              return SelectedValues.Sum(p => p.Price ?? 0);
          }
      }
      
      Custom Body Content

      Use the <BodyContent> instead of the <Content> render fragment if you want to customize the tree item but still use the built-in icons and expansion buttons.

      • .github

      • .vscode

      • content

      • src

        • MudBlazor

        • MudBlazor.Docs

          • _Imports.razor

          • compilerconfig.json

          • MudBlazor.Docs.csproj

          • NewFilesToBuild.txt

        • MudBlazor.Docs.Client

        • MudBlazor.Docs.Compiler

        • MudBlazor.Docs.Server

        • MudBlazor.UnitTests

        • .editorconfig

        • MudBlazor.sln

      • History

      <MudPaper Width="400px" Elevation="2">
          <MudTreeView Items="@TreeItems" Hover="true">
              <ItemTemplate Context="item">
                  <MudTreeViewItem Items="@item.Children" Icon="@item.Icon">
                      <BodyContent>
                          <div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
                              <MudText Style="justify-self: start;">@item.Text</MudText>
                              <div style="justify-self: end;">
                                  <MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Medium" Color="Color.Inherit" />
                                  <MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Medium" Color="Color.Inherit" />
                              </div>
                          </div>
                      </BodyContent>
                  </MudTreeViewItem>
              </ItemTemplate>
          </MudTreeView>
      </MudPaper>
      
      @code {
      
          private List<TreeItemData<string>> TreeItems { get; set; } = [];
      
          public class TreeItemData : TreeItemData<string>
          {
              public TreeItemData(string text, string icon) : base(text)
              {
                  Text = text;
                  Icon = icon;
              }
          }
      
          protected override void OnInitialized()
          {
              TreeItems.Add(new TreeItemData(".github", Icons.Custom.Brands.GitHub));
              TreeItems.Add(new TreeItemData(".vscode", Icons.Custom.Brands.MicrosoftVisualStudio));
              TreeItems.Add(new TreeItemData("content", Icons.Custom.FileFormats.FileDocument));
              TreeItems.Add(new TreeItemData("src", Icons.Custom.FileFormats.FileCode) {
                      Children = [
                          new TreeItemData("MudBlazor", Icons.Custom.Brands.MudBlazor),
                          new TreeItemData("MudBlazor.Docs", Icons.Custom.FileFormats.FileDocument) {
                              Children = [
                                  new TreeItemData("_Imports.razor", Icons.Material.Filled.AlternateEmail),
                                  new TreeItemData("compilerconfig.json", Icons.Custom.FileFormats.FileImage),
                                  new TreeItemData("MudBlazor.Docs.csproj", Icons.Custom.Brands.MicrosoftVisualStudio),
                                  new TreeItemData("NewFilesToBuild.txt", Icons.Custom.FileFormats.FileDocument)
                              ]
                          },
      
                          new TreeItemData("MudBlazor.Docs.Client", Icons.Material.Filled.Folder),
                          new TreeItemData("MudBlazor.Docs.Compiler", Icons.Material.Filled.Folder),
                          new TreeItemData("MudBlazor.Docs.Server", Icons.Material.Filled.Folder),
                          new TreeItemData("MudBlazor.UnitTests", Icons.Material.Filled.Folder),
                          new TreeItemData(".editorconfig", Icons.Custom.FileFormats.FileCode),
                          new TreeItemData("MudBlazor.sln", Icons.Custom.Brands.MicrosoftVisualStudio)
                      ]
              });
              TreeItems.Add(new TreeItemData("History", Icons.Material.Filled.Folder));
          }
      }
      
      An unhandled error has occurred. Reload 🗙