Programmatically populating a ListView in Unity UI Toolkit

Posted by Thomas Neumüller on 2022-04-13

Today I ran into a problem while trying to programmatically populate a ListView element with items in Unity, using the “new” UI Toolkit. The following code throws an exception:

1
2
var listViewLeft = new ListView();
listViewLeft.Add(new Label("Test left"));

⚠️ System.InvalidOperationException: You can’t add directly to this VisualElement. Use hierarchy.Add() if you know what you’re doing.

Naturally, I don’t know what I’m doing, so instead of just adding my child to the hierarchy as this message suggests, I dug deeper. I stumbled upon a post on the Unity forums, where a user has the same problem, and Unity employee uDamian replied with the following:

You cannot add elements directly to ListView. ListView assumes it is in full control of its children as it manages them (it’s a virtual container). You need to create a new custom VisualElement that just inherits from VisualElement and contains a ListView. You can then forward any parameters you wish to your internal ListView.

Some background. The Add() function adds a new child element to the contentContainer element of the parent. By default, the contentContainer = this, so the Add() is trivially adding to the parent. But, custom elements can override this contentContainer to point to some deeper internal child of the parent so all future Add() calls on parent add new children to this deeper element. This can also be used to not allow any children be added to your element by setting contentContainer to null. This is what ListView does.

uDamian on Aug 12, 2020

This reply didn’t help me too much, as it still didn’t answer my question of how I’d add a child to a ListView element. Unfortunately it was quite hard to find the relevant page in the Unity docs, as many search results still yield pages related to the older UI systems (at the rate we’re going, Unity will soon have more legacy UI systems than there are stars in the universe).
If you’re just searching for the relevant page in the Unity docs, here you go.

I will add some more context:

A list view can either be created using the constructor or be provided the relevant properties later.

The constructor takes four arguments:

  1. IList itemsSource
  2. float itemHeight
  3. Func<VisualElement> makeItem
  4. Action<VisualElement, int> bindItem

itemsSource

The itemsSource property, as the name suggests, provides the ListView with content. Technically, this property is the only one that is strictly required, however Unity tries to generate the proper VisualElements from the list by converting them to Labels that contain the string representation of the corresponding ListItem, so it you’re doing something more complex than that, this property alone isn’t enough.

itemHeight

Specifies the fixed height of each item. All items in the list must have the same height. This property can be specified after the creation of the ListView using the fixedItemHeight property.

makeItem

makeItem is a function that returns a VisualElement representing a given list item. It does not yet contain any information such as a title and image for example, it just creates an empty template element that data is later filled into by the bindItem function. If your list doesn’t contain any data, you can stop here.

bindItem

An action that, given a VisualElement and the index of the element in the list, assigns the corresponding piece of data to the element. An example implementation could look like this:

1
Action<VisualElement, int> bindItem = (element, index) => (element as Label).text = listItems[index];

Example

Here is a full example for the creation of a working ListView:

1
2
3
4
5
6
 var listItemsLeft = new List<string> { "test left 1", "test left 2" };
Func<VisualElement> makeItemLeft = () => new Label();
Action<VisualElement, int> bindItemLeft = (element, index) => (element as Label).text = listItemsLeft[index];
var itemHeight = 16;

var listViewLeft = new ListView(listItemsLeft, itemHeight, makeItemLeft, bindItemLeft);

Thoughts or questions? Leave me a comment!