Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved Structs as itemsSource?

Discussion in 'UI Toolkit' started by rod_lopez, Jul 6, 2022.

  1. rod_lopez

    rod_lopez

    Joined:
    Aug 10, 2017
    Posts:
    20
    Hi here!

    Very simple code sample, I have a struct that contains some info for a listView's items. I can add items from script and that works (see line 17).
    But if add a new item through the '+' footer icon (listview.showAddRemoveFooter = true) then I get an exception.
    Now, exactly same code, I change the struct for a string (so items = new List<string>) then the footer's add works without errors.
    The code:

    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UIElements;
    5.  
    6. public struct stringBool
    7. {
    8.    public string text;
    9.    public bool value;
    10. }
    11.  
    12. public class SampleListView : MonoBehaviour
    13. {
    14.    public List<stringBool> items = new List<stringBool>();
    15.    public void Start()
    16.    {
    17.        items.Add(new stringBool {text = "lalal"});
    18.        var rootVisualElement = GetComponent<UIDocument>().rootVisualElement;
    19.        var listView = rootVisualElement.Q<ListView>();
    20.        var listItem = Resources.Load<VisualTreeAsset>("SampleListView");
    21.  
    22.        listView.itemsSource = items;
    23.        listView.makeItem = () =>
    24.        {
    25.            return listItem.Instantiate();
    26.        };
    27.        listView.bindItem = (e, i) =>
    28.        {
    29.            e.Q<Label>().text = items[i].text;
    30.            e.Q<Button>().text = "Button " + items[i].text;
    31.            e.Q<RadioButton>().value = items[i].value;
    32.        };
    33.    }
    34. }
    35.  
    ...and the last lines of the error callstack:

    ArgumentNullException: Value cannot be null.

    Parameter name: item

    System.Collections.Generic.List`1[T].System.Collections.IList.Add (System.Object item) (at <1fe4b731108247d3a7e57fad336611b9>:0)

    UnityEngine.UIElements.ListViewController.AddItems (System.Int32 itemCount) (at /Users/bokken/buildslave/unity/build/ModuleOverrides/com.unity.ui/Core/Collections/Controllers/ListViewController.cs:66)

    UnityEngine.UIElements.ListView.AddItems (System.Int32 itemCount) (at /Users/bokken/buildslave/unity/build/ModuleOverrides/com.unity.ui/Core/Controls/ListView.cs:383)

    UnityEngine.UIElements.ListView.OnAddClicked () (at /Users/bokken/buildslave/unity/build/ModuleOverrides/com.unity.ui/Core/Controls/ListView.cs:447)

    Help?
     
    Last edited: Jul 7, 2022
  2. rod_lopez

    rod_lopez

    Joined:
    Aug 10, 2017
    Posts:
    20
    Some more info... if I use a class instead of a struct it works... better? I don't get an internal error in Unity but I do get the
    listView.bindItem = (e, i) =>
    call above being called with a items.count = 1 **but** a items[0] equals to (null). :|
     
  3. rod_lopez

    rod_lopez

    Joined:
    Aug 10, 2017
    Posts:
    20
    So yeah... in case this helps anybody...

    This works:
    Code (CSharp):
    1.  
    2. public class SampleListView : MonoBehaviour
    3. {
    4.    public class stringBool
    5.    {
    6.        public string text;
    7.        public bool value;
    8.    }
    9.  
    10.    List<stringBool> items = new List<stringBool>();
    11.    public void Start()
    12.    {
    13.        var rootVisualElement = GetComponent<UIDocument>().rootVisualElement;
    14.        var listView = rootVisualElement.Q<ListView>();
    15.        var listItem = Resources.Load<VisualTreeAsset>("SampleListView");
    16.  
    17.        listView.itemsSource = items;
    18.        listView.makeItem = () =>
    19.        {
    20.            return listItem.Instantiate();
    21.        };
    22.        listView.bindItem = (e, i) =>
    23.        {
    24.            items[i] = (items[i] is null)? new stringBool() : items[i];
    25.            e.Q<Label>().text = items[i].text;              
    26.            e.Q<Button>().text = "Button " + items[i].text;
    27.            e.Q<RadioButton>().value = items[i].value;
    28.        };
    29.    }
    30. }
    31.  
    A couple of things from there:
    • If the source is a list of structs it won't work at all and it'll throw an internal error (maybe constrain it to nullable types?)
    • I would imagine the entry created by the footer would be newed BEFORE the bind, but that's not the case

    But anyway, all good, forward we go!
     
    Tamago-Jam likes this.