Hi,
I’m trying to setup a nested ListView’s and have some issues with the bindings. Note that I’m on 2021 LTS so can’t use the new TreeView.
I have tried 2 different approaches but both have issues:
-
I have tried to use set the ‘bindingPath’ for the parent and children ListViews. The top level one works fine but it doesn’t ever bind on the children.
-
I have set the ‘bindingPath’ on the parent list but manually create the children (serializedProperty) array from the serializedObject bound to the root visual element. This works but does not update if the children array is modified via external actions (i.e. via OnSceneGUI). If the overlay is closed and re-opened the change is reflected
This serialzedObject contains a property as such:
[Serializable]
public class EnemySpawnGroup
{
[SerializeField][HideInInspector] protected bool m_IsGroup;
[SerializeField][HideInInspector] protected bool m_Foldout;
[SerializeField][HideInInspector] protected EnemySpawn[] m_Items;
public bool IsGroup => m_IsGroup;
}
[SerializeField] private EnemySpawnGroup[] m_EnemyList;
Here is a cut down version of the overlay code used.
[Overlay(typeof(SceneView), "Enemies Waves")]
public class EnemiesOverlay_Example : Overlay
{
EnemyWaveInfo m_EnemyWaveInfo;
private VisualTreeAsset m_GroupRowTemplate, m_ItemRowTemplate;
private SerializedObject m_SerializedObject;
public override VisualElement CreatePanelContent()
{
var root = new VisualElement { name = "Waves Root" };
var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("path/to/Overlay.uxml");
visualTree.CloneTree(root);
m_GroupRowTemplate = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("path/to/GroupTemplate.uxml");
m_ItemRowTemplate = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("path/to/ChildItemTemplate.uxml");
var so = new SerializedObject(m_EnemyWaveInfo);
root.Bind(so);
m_SerializedObject = so;
Example_BindGroupListView(root);
return root;
}
private void Example_BindGroupListView(VisualElement el)
{
var listView = el.Q<ListView>("groupList");
listView.bindingPath = "m_EnemyList";
listView.makeItem = m_GroupRowTemplate.CloneTree;
// EXAMPLE 1: This works but there are issues with modifications to the child list not updating correctly
listView.bindItem = BindChildListElement_ManualItemSource;
// EXAMPLE 1: This does not bind to the nested ListView elements
listView.bindItem = BindChildListElement_WithBindingPath;
listView.Rebuild();
}
private void BindChildListElement_ManualItemSource(VisualElement el, int groupIndex)
{
var enemyList = m_SerializedObject.FindProperty("m_EnemyList");
var group = enemyList.GetArrayElementAtIndex(groupIndex);
var children = group.FindPropertyRelative("m_Items");
var items = new List<SerializedProperty>(children.arraySize);
for (var j = 0; j < children.arraySize; j++)
{
items.Add(children.GetArrayElementAtIndex(j));
}
var listView = el.Q<ListView>("childList");
listView.itemsSource = items;
listView.makeItem = m_ItemRowTemplate.CloneTree;
listView.bindItem = (element, itemIndex) => { Example_BindGroupRowElement_Manual(element, itemIndex, group); };
listView.Rebuild();
}
private void Example_BindGroupRowElement_Manual(VisualElement el, int itemIndex, SerializedProperty property)
{
Debug.Log($"Example_BindGroupRowElement_Auto {el}, {itemIndex} {property}");
}
private void BindChildListElement_WithBindingPath(VisualElement el, int groupIndex)
{
var listView = el.Q<ListView>("childList");
listView.bindingPath = "m_Items";
listView.makeItem = m_ItemRowTemplate.CloneTree;
listView.bindItem = Example_BindGroupRowElement_BindPath;
listView.Rebuild();
}
private void Example_BindGroupRowElement_BindPath(VisualElement arg1, int arg2)
{
// This never gets called!
Debug.Log($"Example_BindGroupRowElement_Auto {arg1}, {arg2}");
}
}
I would prefer to use the bindingPath only but I cannot seem to get it to work. Is it possible to to so with nested ListView’s? If so what should the binding path be in my case?
If the manual binding/itemsSource is the only option then why are changes not being reflected? I have tried manually rebinding after any child items are added but it doesn’t seem to make any difference.
Any help is appreciated,
Thanks