Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

ListView strange index Problem when scrolling

Discussion in 'UI Toolkit' started by skerkmann, Jan 10, 2020.

  1. skerkmann

    skerkmann

    Joined:
    Oct 18, 2018
    Posts:
    8
    I have a custom Editor for a scriptableObject. This scriptableObject has List i wanted to display and manipulate within a ListView. I tried to model this after the tank example.

    When the list gets big enough that the scrollbar gets visible and i scroll an entrie which is moved out of sight gets inserted at another place in my list.

    At this point my list has only empty entries except the last shown with test:
    upload_2020-1-10_15-7-57.png
    after scrolling up it this entrie is somehow also in a random top entrie:
    upload_2020-1-10_15-9-33.png
    and the bottom one is still present.

    Here my code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5. using UnityEngine.UIElements;
    6.  
    7. [CustomEditor(typeof(TemplatePathsSO))]
    8. public class TemplatePathsSOEditor : Editor
    9. {
    10.     private TemplatePathsSO templatePathsSO;
    11.     private VisualElement root;
    12.     private VisualTreeAsset visualTree;
    13.  
    14.  
    15.     private ListView templateList;
    16.  
    17.     public void OnEnable(){
    18.         templatePathsSO = (TemplatePathsSO)target;
    19.  
    20.         root = new VisualElement();
    21.         visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/TemplatePathsSOUI.uxml");
    22.     }
    23.  
    24.     public override VisualElement CreateInspectorGUI()
    25.     {
    26.         root.Clear();
    27.         Button plusButton = new Button();
    28.         plusButton.name = "plusButton";
    29.         plusButton.text = "+";
    30.         if (plusButton != null)
    31.         {
    32.             plusButton.clickable.clicked += () =>
    33.             {
    34.                 templatePathsSO.templatePaths.Insert(0, new TemplatePath());
    35.                 templateList.Refresh();
    36.             };
    37.         }
    38.         root.Add(plusButton);
    39.  
    40.         visualTree.CloneTree(root);
    41.  
    42.         templateList = root.Q<ListView>("templateList");
    43.         if (templateList != null)
    44.         {
    45.             templateList.selectionType = SelectionType.None;
    46.  
    47.             if (templateList.makeItem == null)
    48.                 templateList.makeItem = MakeItem;
    49.             if (templateList.bindItem == null)
    50.                 templateList.bindItem = BindItem;
    51.  
    52.             templateList.itemsSource = templatePathsSO.templatePaths;
    53.             templateList.Refresh();
    54.         }
    55.  
    56.         return root;
    57.     }
    58.  
    59.       private VisualElement MakeItem()
    60.     {
    61.         var element = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/TemplatePathUI.uxml").CloneTree();
    62.  
    63.         element.style.flexDirection = FlexDirection.Row;
    64.         element.style.height = 20;
    65.  
    66.         Button plusButton = new Button();
    67.         plusButton.name = "plusButton";
    68.         plusButton.text = "+";
    69.         element.Add(plusButton);
    70.  
    71.         Button minusButton = new Button();
    72.         minusButton.name = "minusButton";
    73.         minusButton.text = "-";
    74.         element.Add(minusButton);
    75.  
    76.         return element;
    77.     }
    78.  
    79.     private void BindItem(VisualElement element, int index)
    80.     {
    81.         TemplatePath templatePath = templatePathsSO.templatePaths[index];
    82.  
    83.         TextField templateName;
    84.         templateName = element.Q<TextField>("templateName");
    85.         templateName.value = templatePath.templateName;
    86.         templateName.RegisterValueChangedCallback( ctx => {
    87.             templatePath.templateName = (string) ctx.newValue;
    88.         });
    89.  
    90.         TextField pathString;
    91.         pathString = element.Q<TextField>("pathString");
    92.         pathString.value = templatePath.pathString;
    93.         pathString.RegisterValueChangedCallback( ctx => {
    94.             templatePath.pathString = (string) ctx.newValue;
    95.         });
    96.  
    97.         var selectButton = element.Q<Button>("selectPath");
    98.         if (selectButton != null)
    99.         {
    100.             selectButton.clickable.clicked += () =>
    101.             {
    102.                 string path = EditorUtility.OpenFilePanel("Choose Template", "Assets", "txt");
    103.                 if (path.Length > 0)
    104.                 {
    105.                     templatePath.pathString = path;
    106.                     pathString.value = templatePath.pathString;
    107.                 }
    108.             };
    109.         }
    110.  
    111.         var plusButton = element.Q<Button>("plusButton");
    112.         if (plusButton != null)
    113.         {
    114.             plusButton.clickable.clicked += () =>
    115.             {
    116.                 templatePathsSO.templatePaths.Insert(index + 1, new TemplatePath());
    117.                 templateList.Refresh();
    118.             };
    119.         }
    120.  
    121.         var minusButton = element.Q<Button>("minusButton");
    122.         if (minusButton != null)
    123.         {
    124.             minusButton.clickable.clicked += () =>
    125.             {
    126.                 templatePathsSO.templatePaths.RemoveAt(index);
    127.                 templateList.Refresh();
    128.             };
    129.         }
    130.     }
    131. }
     
  2. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    771
    Hello,

    Which Unity version are you using ?
     
  3. skerkmann

    skerkmann

    Joined:
    Oct 18, 2018
    Posts:
    8
    hey antoine,

    im using 2019.3.0f3
     
  4. skerkmann

    skerkmann

    Joined:
    Oct 18, 2018
    Posts:
    8
    another thing i encounter is that i have to use
    Code (CSharp):
    1. EditorUtility.SetDirty(templatePathsSO);
    after each change i do over a binding. Without it looses the values when i close unity. But this doesn't fix my scroll issue.
     
  5. Shaderic

    Shaderic

    Joined:
    Dec 8, 2017
    Posts:
    5
    I have the same problem in Unity 2019.3.0f3 when using a scriptable object for my ListView. In the bind item method, it seems that the wrong index is used.
     
  6. SaschaMoos19997

    SaschaMoos19997

    Joined:
    Sep 1, 2021
    Posts:
    3
    Any updates here? I encountered the same problem. I use Unity 2021.1.28f1 and the 1.0.0-preview.18 UI Toolkit and 1.0.0-preview.18 UI Builder packages.
     
  7. griendeau_unity

    griendeau_unity

    Unity Technologies

    Joined:
    Aug 25, 2020
    Posts:
    247
    The problem in the example posted by the OP I think is that callbacks are registered multiple times on the same field. In a ListView, elements are recycled as users scroll. The `
    bindItem
    ` is called again as we scroll with a new index. So it is the same field that is getting the RegisterValueChangedCallback. Callbacks should instead be register in the `
    makeItem
    ` phase, then in the `
    bindItem
    ` you could store relevant information in `
    userData
    `. Or else, it is also possible to unregister the callbacks in the `
    unbindItem
    ` phase.

    When binding to a ScriptableObject with `BindProperty()` or Bind(), there is usually no need to override the
    bindItem
    . The binding system will take care of generating the required controls. But if you do, make sure you take into account that the list might contain an additional item at index 0 for the array size. To remove that item from the data list, you can set
    showBoundCollectionSize
    to false
     
  8. SaschaMoos19997

    SaschaMoos19997

    Joined:
    Sep 1, 2021
    Posts:
    3
    Thanks a lot, this solved my issue.