TreeView

  • All collections widgets support virtualization: gameobjects created only for the visible items.

  • Add Selectable component to use keyboard and gamepad navigation.

Attention

Different TreeView’s cannot display the same nodes, unlike ListView, TileView, and Table.

Options

Options are almost same as the ListView, TileView and Table.

  • Nodes ObservableList<TreeNode<TItem>>

    Not available in the inspector window.

  • Deselect Collapsed Nodes bool

    Deselect nested nodes when parent node collapsed.

  • Scroll With Indent bool

    Scrolling with node indent in the secondary direction.

  • Container Max Size bool

    Prevent scrollbar blink caused by virtualization: the container will have the maximum width of all items. By default, the container has the maximum width of only visible items.
    Require List Type = List View with Variable Size.

Get nodes

public TreeView Tree;

ObservableList<TreeNode<TreeViewItem>> nodes;

void Start()
{
   if (Tree.Nodes == null)
   {
      Tree.Nodes = new ObservableList<TreeNode<TreeViewItem>>();
   }

   nodes = Tree.Nodes;
}

Get selected nodes

Tree.SelectedNodes.ForEach(x =>
{
   // do something with selected node
   Debug.Log(x.Item.Name);

   var component = Tree.GetItemComponent(x.Index);

   // not displayed component will be null
   if (component != null)
   {
      component.DoSomething();
   }
});

Add listeners

void AddListeners()
{
   Tree.NodeSelected.AddListener(ProcessSelectedNode);

   Tree.NodeDeselected.AddListener(ProcessDeselectedNode);
}

void ProcessSelectedNode(TreeNode<TreeViewItem> node)
{
   Debug.Log("selected: " + node.Item.Name);
}

void void ProcessDeselectedNode(TreeNode<TreeViewItem> node)
{
   Debug.Log("deselected: " + node.Item.Name);
}

Select node

Tree.SelectNode(nodes[1].Nodes[0]);

Select node with subnodes

Tree.SelectNodeWithSubnodes(nodes[1].Nodes[1]);

Deselect node

Tree.DeselectNode(nodes[1].Nodes[0]);

Deselect node with subnodes

Tree.DeselectNodeWithSubnodes(nodes[1].Nodes[1]);

Scroll to node

Tree.ScrollToAnimated(node);

Add node

var test_item = new TreeViewItem("added");
var test_node = new TreeNode<TreeViewItem>(test_item);
nodes.Add(test_node);

Hide nodes

nodes[1].IsVisible = false;
nodes[2].Nodes[1].IsVisible = false;

Collapse node

nodes[0].Nodes[0].IsExpanded = false;

Expand node

nodes[0].Nodes[0].IsExpanded = true;

Change node name

nodes[0].Item.Name = "Node renamed from code";
nodes[0].Nodes[1].Item.Name = "Another node renamed from code";

Sort

// Compare nodes by Name in ascending order
Comparison<TreeNode<TreeViewItem>> comparisonAsc = (x, y) => x.Item.Name.CompareTo(y.Item.Name);

// Compare nodes by Name in descending order
Comparison<TreeNode<TreeViewItem>> comparisonDesc = (x, y) => -x.Item.Name.CompareTo(y.Item.Name);

public void SortAsc()
{
   using var _ = nodes.BeginUpdate();
   ApplyNodesSort(nodes, comparisonAsc);
}

public void SortDesc()
{
   using var _ = nodes.BeginUpdate();
   ApplyNodesSort(nodes, comparisonDesc);
}

void ApplyNodesSort<T>(ObservableList<TreeNode<T>> nodes, Comparison<TreeNode<T>> comparison)
{
   // apply sort for current nodes
   nodes.Sort(comparison);
   // apply sort for child nodes
   nodes.ForEach(node =>
   {
      if (node.Nodes != null)
      {
         ApplyNodesSort(node.Nodes as ObservableList<TreeNode<T>>, comparison);
      }
   });
}

Filter nodes

public void Filter(string nameContains)
{
   // Maintains performance while items are added/removed/changed
   // by preventing the widgets from drawing
   // until the EndUpdate() method is called
   // or after value returned by BeginUpdate() was disposed.
   using var _ = nodes.BeginUpdate();

   SampleFilter(nodes, x => x.Name.Contains(nameContains));
}

bool SampleFilter(IObservableList<TreeNode<TreeViewItem>> nodes, Func<TreeViewItem,bool> filterFunc)
{
   return nodes.Count(x =>
   {
      var have_visible_children = (x.Nodes==null) ? false : SampleFilter(x.Nodes, filterFunc);
      x.IsVisible = have_visible_children || filterFunc(x.Item);
      return x.IsVisible;
   }) > 0;
}

Reset filter

public void ResetFilter()
{
   using var _ = nodes.BeginUpdate();
   nodes.ForEach(SetVisible);
}

void SetVisible(TreeNode<TreeViewItem> node)
{
   if (node.Nodes != null)
   {
      node.Nodes.ForEach(SetVisible);
   }

   node.IsVisible = true;
}

Clear nodes

public void Clear()
{
   nodes.Clear();
}

Nodes Serialization

You can use helper class TreeNodeJson<TItem> for the node serialization and deserialization.

Warning

Unity JsonUtility does not support recursive types so it cannot be used. Newtonsoft.Json can be used instead.

// serialize
var nodes = TreeNodeJson<TreeViewItem>.ConvertNodes(TreeView.Nodes);
var json = JsonConvert.SerializeObject(nodes);

// deserialize
var decoded = JsonConvert.DeserializeObject<TreeNodeJson<TreeViewItem>[]>(json);
TreeView.Nodes = TreeNodeJson<TreeViewItem>.ConvertNodes(decoded);