Search Unity

Feature Request Way to force ListView update without a full Refresh.

Discussion in 'UI Toolkit' started by recursive, Jul 20, 2019.

  1. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    Is there a way to update ListView without calling a Refresh (and thus destroying / recreating everything?) I've got a debug system that uses ListView to display data from ECS components in a more friendly format, but the only way I've gotten it to repaint the views is by calling Refresh() every frame.

    Is there a better way to do this? I would MarkDirtyRepaint() should do something but it doesn't seem to change anything. Or is there something I'm missing?
     
  2. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    Let me know if I understand correctly: You want to update the values displayed in the list view every frame to the current ComponentData values.

    The cleanest way to do this would be some kind of one-way data-binding, but since the current in-editor DataBindings only supports SerializedObjects/Properties, you will need to poll your ComponentData every frame and assign the values to the VisualElements as they change. If your items UI is simple, it might just involve to use the scheduler to recall your bindItem delegate every frame.

    However, if you're using PropertyFields, that might not be the best way performance-wise as the PropertyField currently recreates the actual UI everytime you rebind. Then a mix of UQueries to find the children to update might do the trick.

    Something like this:

    Code (CSharp):
    1.     public class ComponentDataListItem : VisualElement
    2.     {
    3.         public int BoundItemIndex{get;set;}
    4.  
    5.         ListView mParentView;
    6.  
    7.         public ComponentDataListItem(ListView parent)
    8.         {
    9.             BoundItemIndex = -1;
    10.             mParentView = parent;
    11.             schedule.Execute( ()=> UpdateValues()).Every(10); //we'll poll values every frame when we're added to the hierarchy
    12.         }
    13.  
    14.         public void UpdateValues()
    15.         {
    16.             if(BoundItemIndex >= 0 && mParentView != null)
    17.             {
    18.                 object current = mParentView.itemsSource[BoundItemIndex];
    19.  
    20.                 //TODO: Update elements values here, ie set a label's text, etc
    21.             }
    22.         }
    23.  
    24.     }
    25.  
    26.     class ComponentDataViewer: EditorWindow
    27.     {
    28.  
    29.         void OnEnable()
    30.         {
    31.             //[...]
    32.             ListView lv = rootElement.Q<ListView>();
    33.  
    34.             lv.makeItem = () => new ComponentDataListItem(lv);
    35.  
    36.             lv.bindItem = (element, index) =>
    37.             {
    38.                 var item = element as ComponentDataListItem;
    39.  
    40.                 item.BoundItemIndex = index;
    41.                 item.UpdateValues();
    42.             };
    43.         }
    44.     }
     
  3. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    It's the former. The elements are non interactive. I want to basically execute the height change code without doing a full refresh (as the list binding isnt changing just the elements themselves).
     
  4. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    Not sure I understand your needs correctly. Can you provide a small snippet so I can figure out what you're trying to achieve?

    The ListView currently requires a fixed height for its elements. Setting ListView.itemHeight should properly refresh the items. However, you need to make sure not to set it to the same value as it will trigger a refresh every time even if the height has not changed. This behavior is less than ideal and will be improved in the future.
     
  5. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    I've attached a zip that contains both of the files that make up my Input Debugger. Let me know if you need a smaller snippet or more context.

    Right now I'm using EditorApplication.update and an update flag to call Refresh() less frequently, but if there's a way to force a rebind without dumping the VisualElement pool, that'd be super.
     

    Attached Files: