(Almost) Codeless Editor Window UI Toolkit ListView Binding

I’m posting this both to ask a couple of questions, and to put up an example that I couldn’t find. I have a working example of and almost codeless Editor Window with a ListView that binds to a ScriptableObject list of custom objects. I’ll post the (long) example first and put the questions at the end.

  1. Create a ScriptableObject as the data source
    1. Right-click on the directory where you want to create the ScriptableObject, and Select Create > Scriptable Objects > <ScriptableObject Name> (e.g. TaskDataSO)
using System;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "TaskDataSO", menuName = "Scriptable Objects/TaskDataSO")]

public class TaskDataSO : ScriptableObject
{
  public List<Task> taskList = new();

  [Serializable]
  public class Task
  {
    public string taskName;
    public bool   completed;
  }
}
  1. Create an Item Template
    1. Select Window > UI Toolkit > UI Builder
    2. Select the UXML document in the Hierarchy
    3. Check Document Settings > Editor Extension Authoring
    4. At the top of the Viewport, using the Theme dropdown, select Active Editor Theme
    5. Drag and drop the Visual Element you want to bind
      1. Click the 3 dots next to the Value field and select Add Binding…
        1. In Data Source Path, type the name of the field you want to bind (e.g. taskName)
        2. Using the Binding Mode dropdown, select Two Way
        3. Click Add Binding
        4. Ignore the Console spam (The spam will stop once you close the UI Builder)
      2. Repeat for each Visual Element that you want to bind
    6. At the top of the Viewport, using the File dropdown, select Save
      1. NOTE: The UXML namespace may be ui or engine. It seems that if you create the UXML document by using File > New from the UI builder, you get ui and if you right-click in the Unity editor to create the UXML document, you get engine. It seems to work either way as long as you are consistent. I don’t know what the difference is.
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
    <ui:TextField label="Task Name">
        <Bindings>
            <ui:DataBinding property="itemsSource" data-source-path="taskName" binding-mode="TwoWay" />
        </Bindings>
    </ui:TextField>
    <ui:Toggle label="Completed">
        <Bindings>
            <ui:DataBinding property="value" data-source-path="isCompleted" binding-mode="TwoWay" />
        </Bindings>
    </ui:Toggle>
</ui:UXML>
  1. Create the ListView UI
    1. Select Window > UI Toolkit > UI Builder
    2. At the top of the Viewport, using the File dropdown, select New
    3. Select the uxml document in the Hierarchy
    4. Check Document Settings > Editor Extension Authoring
    5. At the top of the Viewport, using the Theme dropdown, select Active Editor Theme
    6. Drag and drop the List View you want to bind
      1. Under Bindings > Data Source, make sure that Object is selected and in the object field, select the ScriptableObject that you created in step 1.1
      2. Using the Virtualization Method dropdown, select Dynamic Height
      3. Check Show Add Remove Footer
      4. Using the Binding Source Selection Mode dropdown, select Auto Assign
      5. In the Item Template object field, select the Item Template that you created in step 2 (e.g. TaskListItemTemplate)
    7. At the top of the Viewport, using the File dropdown, select Save
    8. Drag the UXML Preview at the bottom up to expand the pane and click the open icon in the upper right corner to open the UXML in your IDE (NOTE: Make sure the namspace on the UXML tags is the same as in the Item Template created in step 2, or it will not work)
      1. Locate the ListView tag (may be in the ui or engine namespace)
      2. Delete the closing slash at the end of the tag
      3. Create a closing tag for the ListView tag:
        1. </ui:ListView> or </engine:ListView> depending on which namespace the UI Builder picked (NOTE: make sure you match the namespace to the rest of the tags or it will not work)
      4. Above the closing tag you just added, paste these UXML tags
        1. Replace <Name of the List in the ScriptableObject> with the name of the list you declared in your ScriptableObject (e.g. taskList)
        2. Save the UXML file
<Bindings>
    <engine:DataBinding property="itemsSource" data-source-path="<Name of the List in the ScriptableObject>" binding-mode="TwoWay" />
</Bindings>

Here is the whole ListView UXML file (NOTE: The IDs will be different for you):

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="True">
    <ui:ListView data-source="project://database/Assets/ScriptableObjects/TaskDataSO.asset?fileID=11400000&amp;guid=63a1b58cf6ade3f409acec511a95a017&amp;type=2#TaskDataSO" item-template="project://database/Assets/UI/TaskListItemTemplate.uxml?fileID=9197481963319205126&amp;guid=3000deaa4c6f91049a7e703c2a621c4f&amp;type=3#TaskListItemTemplate">
        <Bindings>
            <ui:DataBinding property="itemsSource" data-source-path="taskList" binding-mode="TwoWay" />
        </Bindings>
    </ui:ListView>
</ui:UXML>
  1. Create the EditorWindow script
    1. Create a folder named Editor (EditorWindow scripts need to be in a folder named Editor in order to work)
    2. Right click on the Editor folder and select Create > Scripting > MonoBehaviour Script
      1. Name the script what you want (e.g. TaskListEditorWindow)
      2. Open the file in your IDE
      3. Paste in this code and save the file:
using System.IO;
using UnityEditor;
using UnityEngine.UIElements;

public class TaskListEditorWindow : EditorWindow
{
  private TaskDataSO      vortexToDoData;
  private VisualTreeAsset vortexToDoSettingsUIVisualTreeAsset;

  [MenuItem("Tools/Task List")]
  public static void OpenWindow()
  {
    GetWindow<TaskListEditorWindow>("Task List");
  }

  private void OnEnable()
  {
    LoadTaskListViewUI();
  }

  private void LoadTaskListViewUI()
  {
    string TaskListUIVisualTreeAssetFilename = "TaskListViewUI";

    string[] guids = AssetDatabase.FindAssets($"{TaskListUIVisualTreeAssetFilename} t:VisualTreeAsset");

    if (guids.Length != 1)
    {

      throw new FileNotFoundException($"Could not find {TaskListUIVisualTreeAssetFilename}.uxml");

    }

    string vortexToDoSettingsUIVisualTreeAssetPath = AssetDatabase.GUIDToAssetPath(guids[0]);

    this.vortexToDoSettingsUIVisualTreeAsset      =

      AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(vortexToDoSettingsUIVisualTreeAssetPath);

    VisualElement rootVisualElement = this.rootVisualElement;

    TemplateContainer vortexToDoSettingsUITemplateContainer = this.vortexToDoSettingsUIVisualTreeAsset.Instantiate();
    rootVisualElement.Add(vortexToDoSettingsUITemplateContainer);
  }
}

  1. Testing
    1. Right click on the ScriptableObject (e.g. TaskDataSO) and select Properties
    2. In the Unity Editor, select Tools > Task List
      2025-05-26 18_27_52-DesktopWindowXamlSource - Tools - Task List
      1. In the newly opened Task List Editor Window, click on the + button (This will generate Console spam until data is entered)
        1. You should see that a new record was created in both the Task List Editor Window, and the Properties window
        2. Type in a Task Name
        3. Click Completed
        4. You should see the changes reflected in the Properties window
        5. Any changes you make in the Properties window should be reflected in the Task List Editor Window
        6. Repeat as many times as desired

So, my questions are:

  1. Why do you get a different namespace in the UXML depending on if you right click to create it (engine) or if you create it in the UI Builder (ui)?
  2. How can I get rid of the console spam when creating a new record (empty Task object) in the bound ListView? e.g.:

[UI Toolkit] Could not retrieve the value at path ‘[3].taskName’ for source of type ‘List`1’: the path from the source to the target is either invalid or contains a null value. (Builder)
UnityEditor.RetainedMode:UpdateSchedulers ()

[UI Toolkit] Could not retrieve the value at path ‘[3].isCompleted’ for source of type ‘List`1’: the path from the source to the target is either invalid or contains a null value. (Builder)
UnityEditor.RetainedMode:UpdateSchedulers ()

I hope this helps someone. Thanks!

1 Like

I feel like if you only need the default editor ListView behaviour, then a PropertyField will be more simple and get the same result with less work.

If you do need custom behaviour, then you’ll be needing to code it anyway.