Search Unity

How to Make an Item in a ListView from a uxml template?

Discussion in 'UI Toolkit' started by Jean-Fabre, Apr 28, 2021.

  1. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi,

    I can't find my way into making an Item for a ListView using a uxml template that I loaded from the Resources.

    all examples I find build the item via code, but using uxml allows me to make a complex cell easily, it will have many labels, few buttons, and probably more.

    I tried CloneTree() and Instantiate() but they are not working...

    Code (CSharp):
    1.        
    2.  
    3. void MakeTempListContent()
    4.         {
    5.             ListView listView = rootVisualElement.Q<ListView>("ListView");
    6.             VisualTreeAsset ListItem = Resources.Load<VisualTreeAsset>("ListItemTemplate");
    7.  
    8.             const int itemCount = 30;
    9.             var items = new List<string>(itemCount);
    10.             for (int i = 1; i <= itemCount; i++)
    11.                 items.Add(i.ToString());
    12.  
    13.  
    14.             listView.makeItem = () => ListItem.CloneTree(); // doesn't work, and ListItem.Instantiate() neither.
    15.  
    16.             //listView.bindItem = (e, i) => (e as Label).text = items[i];
    17.  
    18.             listView.onItemChosen += obj => Debug.Log(obj);
    19.             listView.onSelectionChanged += objects => Debug.Log(objects);
    20.  
    21.             listView.itemsSource = items;
    22.         }
    23.  
    Thanks for the help,

    Bye,

    Jean
     
  2. AlainL_Unity

    AlainL_Unity

    Joined:
    Apr 6, 2021
    Posts:
    6
    Hi Jean-Fabre,

    Please have a look at the "List View" sample under Window > UI Toolkit > Samples.

    Regards,
    Alain
     
    t2g4 likes this.
  3. Jean-Fabre

    Jean-Fabre

    Joined:
    Sep 6, 2007
    Posts:
    429
    Hi, the sample is not using a uxml as a template being injected in the makeItem() function.



    Bye,

    Jean
     
  4. AlainL_Unity

    AlainL_Unity

    Joined:
    Apr 6, 2021
    Posts:
    6
    Hi Jean,

    You are right, let’s try again.

    In order for this to work, the bindItem has to be defined. Also, the .uxml file for the ListView needs to define the view and entry height (--unity-item-height).

    Here’s a complete, working example:

    Main.uxml
    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
    2.     <ui:ListView focusable="true" style="flex-grow: 1; --unity-item-height: 32; height: 400px;" />
    3. </ui:UXML>
    SampleListView.uxml
    Code (CSharp):
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <engine:UXML
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4.     xmlns:engine="UnityEngine.UIElements"
    5.     xmlns:editor="UnityEditor.UIElements"
    6.     xsi:noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"
    7. >
    8.     <engine:Label text="Hello World! From UXML" />
    9.     <engine:Button text="Button" />
    10. </engine:UXML>
    SampleListView.uss
    Code (CSharp):
    1. Label {
    2.     font-size: 20px;
    3.     -unity-font-style: bold;
    4.     color: rgb(68, 138, 255);
    5. }
    SampleListView.cs
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.UIElements;
    6. using UnityEditor.UIElements;
    7.  
    8. public class SampleListView : EditorWindow
    9. {
    10.     [MenuItem("Window/UI Toolkit/SampleListView")]
    11.     public static void ShowExample()
    12.     {
    13.         SampleListView wnd = GetWindow<SampleListView>();
    14.         wnd.titleContent = new GUIContent("SampleListView");
    15.     }
    16.  
    17.     public void CreateGUI()
    18.     {
    19.         var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/Main.uxml");
    20.         VisualElement main = visualTree.Instantiate();
    21.         rootVisualElement.Add(main);
    22.      
    23.         // Create some list of data, here simply numbers in interval [1, 1000]
    24.         const int itemCount = 1000;
    25.         var items = new List<string>(itemCount);
    26.         for (int i = 1; i <= itemCount; i++)
    27.             items.Add(i.ToString());
    28.  
    29.         var listItem = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/SampleListView.uxml");
    30.  
    31.         Func<VisualElement> makeItem = () => listItem.Instantiate();
    32.  
    33.         Action<VisualElement, int> bindItem = (e, i) =>
    34.         {
    35.             var label = e.Q<Label>();
    36.             label.text = items[i];
    37.          
    38.             var button = e.Q<Button>();
    39.             button.text = "Button " + items[i];
    40.         };
    41.  
    42.         var listView = main.Q<ListView>();
    43.         listView.makeItem = makeItem;
    44.         listView.bindItem = bindItem;
    45.         listView.itemsSource = items;
    46.         listView.selectionType = SelectionType.Multiple;
    47.     }
    48. }
    Regards,
    Alain
     
  5. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    397
    This only works in the editor since AssetDatabase is in the Editor namespace.

    For runtime use I tried Resources.Load<VisualTreeAsset>("myAsset") which worked.

    But I run into a small problem:
    The Rootelement of myAsset.uxml is of type "Button", but "item.Instantiate() only works for VisualElement.
    is there a way to have Button as root element?
     
  6. gareth_untether

    gareth_untether

    Joined:
    Jan 5, 2018
    Posts:
    69
    Hey manuelgoellnitz

    Would it be possible to share your code? I can't find any examples of using ListView at runtime.

    Cheers
     
  7. manuelgoellnitz

    manuelgoellnitz

    Joined:
    Feb 15, 2017
    Posts:
    397
    I don't have a custom listview either what i meant is creating ui elements that are based on custom uxmls. I do this basically like in the examples posted here.
    But insead of

    Code (CSharp):
    1. var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/Main.uxml");
    I use


    Code (CSharp):
    1. var visualTree = Resources.Load<VisualTreeAsset>("Assets/Editor/Main.uxml");
    The problem of this method is, that unity creates a Wrapper VisualElement around the content of the uxml.
     
  8. gareth_untether

    gareth_untether

    Joined:
    Jan 5, 2018
    Posts:
    69
    Ok thanks for your reply!
     
  9. Skflowne

    Skflowne

    Joined:
    Apr 6, 2014
    Posts:
    14
    Is it possible to do this with custom editors ?

    I have a Spawner with an Wave array, these waves also have some WaveTiming objects inside them.

    I'd like to define and reuse the WaveEditor and WaveTimingEditor while having a ListView handling the waves at the SpawnerEditor level.

    It's very unclear to me how to make this work. I was expecting just creating the custom WaveEditor would change the rendering inside the ListView of the SpawnerEditor but it does not.

    Code (CSharp):
    1. [CustomEditor(typeof(Wave))]
    2. public class WaveEditor : Editor
    3. {
    4.   public VisualTreeAsset tree;
    5.  
    6.   public override VisualElement CreateInspectorGUI()
    7.   {
    8.     VisualElement customInspector = new VisualElement();
    9.     tree.CloneTree(customInspector);
    10.  
    11.     return customInspector;
    12.   }
    13. }
    14.  
    I have set the public tree variable to my UXMLWave file which just has MinMaxSlider, so I'd expect to see it instead of the default inspector for each item.

    Code (CSharp):
    1.  
    2. [CustomEditor(typeof(OffscreenSpawner))]
    3. public class OffscreenSpawnerEditor : Editor
    4. {
    5.   const string path = "Editors/OffscreenSpawner";
    6.   const string editorTemplateName = "offscreen-spawner-editor";
    7.   const string editorStyleName = "offscreen-spawner-editor-style";
    8.   const string waveEditorTemplateName = "UXMLWave";
    9.  
    10.   public override VisualElement CreateInspectorGUI()
    11.   {
    12.     VisualElement customInspector = new VisualElement();
    13.  
    14.     VisualTreeAsset editorTemplate = Resources.Load(GetPath(editorTemplateName)) as VisualTreeAsset;
    15.     editorTemplate.CloneTree(customInspector);
    16.  
    17.     customInspector.styleSheets.Add(Resources.Load(GetPath(editorStyleName)) as StyleSheet);
    18.  
    19.     return customInspector;
    20.   }
    21.  
    22.   private string GetPath(string fileName)
    23.   {
    24.     return $"{path}/{fileName}";
    25.   }
    26. }
    27.  
    And this one's uxml only has a ListView that displays the waves array but I'm just seeing the default editor for my Wave items.
     
  10. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598