Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  3. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Can you use the new UI Toolkit Data Binding on List Views?

Discussion in '2023.2 Beta' started by SkywardAssembly, Jun 16, 2023.

  1. SkywardAssembly

    SkywardAssembly

    Joined:
    Dec 11, 2022
    Posts:
    4
    The new binding system looks great and there were a lot of clarifications in this thread already, but I haven't found a way to bind a collection to a list view or similar, is there a way to do this?

    Looking forward to the announcement post :)
     
    Yuchen_Chang likes this.
  2. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hi @SkywardAssembly!

    Sorry, I didn't catch this post quickly. So the announcement has been made here.

    Regarding the
    ListView
    , I would say that it's partially supported in the runtime bindings system. I say partially because you can use the
    DataBinding
    type to create bindings between your data and the bindable properties of the
    ListView
    , however the
    ListView
    requires a little bit more logic to function properly.

    So the best way to use the new system with the
    ListView
    control is to do the following:
    1. Create a
      VisualTreeAsset
      to contain the UI of the list item.
    2. Set the asset created in 1. as the
      itemTemplate
      of the
      ListView
      . This is a new property that we added to make it easier to setup the
      ListView
      and to use the new binding system.
    3. Set the
      bindingSourceSelectionMode
      to
      AutoAssign
      . What this will do is that it will set the
      itemsSource
      as the
      dataSource
      of each item and it will prefix the
      dataSourcePath
      with the correct index.
    You can use bindings to set the
    itemsSource
    itself and the other various properties of the
    ListView
    . You should note however that the
    itemsSource
    property is not available in the UI Builder, so the binding must be manually added in the UXML file.

    What makes the support partial is:
    • It won't call the
      ListView.RefreshItems()
      or
      ListView.Rebuild()
      for you whenever the list size changes. Depending on what you are doing, this can be achieved by creating a
      CustomBinding
      that tracks the size of the
      itemsSource
      and call refresh whenever it changes.
    • If the
      ListView
      is refreshed or rebuilt after the binding operations has been completed, your
      ListView
      might be temporarily displaying outdated information.
    This will be part of the "Current limitations & what's next" part of the announcement, but I haven't had time yet to write it.

    Hope this helps!
     
    Last edited: Jul 6, 2023
    tang001, Yuchen_Chang and Anthiese like this.
  3. SkywardAssembly

    SkywardAssembly

    Joined:
    Dec 11, 2022
    Posts:
    4
    Thank you for getting back to us!
    We were able to create a custom element that handles the refresh and rebuild with this information
     
    martinpa_unity likes this.
  4. StefanDu

    StefanDu

    Joined:
    Mar 29, 2020
    Posts:
    3

    Hi martinpa,

    do you habe an example for that binding? I don't get it to work. I tried it with code like following in a custom binding.

    Code (CSharp):
    1. protected override void OnDataSourceChanged(in DataSourceContextChanged context)
    2.     {
    3.         Debug.Log("Context changed");
    4.         var listview = context.targetElement as ListView;
    5.  
    6.         listview.itemsAdded += (obj) =>
    7.         {
    8.             Debug.Log("Item added");
    9.             listview.Rebuild();
    10.         };
    11.         listview.itemsRemoved += (obj) =>
    12.         {
    13.             Debug.Log("Item removed");
    14.             listview.Rebuild();
    15.         };
    16.     }
    I get the "Context changed" message, but not the other two messages.
     
  5. CarlosAI

    CarlosAI

    Joined:
    May 9, 2023
    Posts:
    122
    Same question! :)
     
  6. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hi @StefanDu, the
    OnDataSourceChanged
    will be called when the resolved data source itself has changed and not when its sub-values are changed. For the
    ListView
    's callbacks, you will only get those when items are added or removed through the
    ListView
    itself. So if you are adding and removing items through the list directly, the callbacks won't be called.

    So, in your custom binding, you will need to track the list count to perform a refresh of the
    ListView
    when it has changed.

    To make this automatic, we would need some concept of an observable list and a list view that can operate on those. Sadly, we do not have this at the moment.

    Hope this helps!
     
  7. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hi @StefanDu,

    I've attached a small, self-contained example of such a binding.

    Hope this helps!
     

    Attached Files:

    tertle, Knedlo and CarlosAI like this.
  8. Knedlo

    Knedlo

    Joined:
    Oct 15, 2012
    Posts:
    54
    Hello @martinpa_unity, thanks for the example, it was helpful.

    I have found a bug related to it, I think. The example works fine, but when I implement INotifyBindablePropertyChanged in my data source it prevents calls to update on the ListViewCountTracker. Implementing IDataSourceViewHashProvider is fine and works. From what I see, all custom bindings should update every frame by default and the example doesn't seem to be changing that. I haven't try it on your whole example, just copied the ListViewCountTracker to my project and used it.
     
  9. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hey @Knedlo, yes, I believe you may have found a bug. It seems that we are not checking that the
    updateTrigger
    is
    BindingUpdateTrigger.OnSourceChanged
    before skipping updates based on the changes reported by the
    INotifyBindablePropertyChanged
    interface.

    I'll get that fixed right away.

    Thank you for reporting it.
     
  10. Knedlo

    Knedlo

    Joined:
    Oct 15, 2012
    Posts:
    54
    Glad to help. Just in case anyone is looking for a workaround in the meantime... it is possible to dispatch the propertyChanged with empty string which results in an update of ListViewCountTracker (or any CustomBinding, I presume).
     
  11. Fribur

    Fribur

    Joined:
    Jan 5, 2019
    Posts:
    141
  12. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hi @Fribur, this looks like a bug, can you report it using "Help \> Report a bug..." ?
    Thanks!
     
  13. Fribur

    Fribur

    Joined:
    Jan 5, 2019
    Posts:
    141
    Thanks and no. Most bug reports I open get closed, so do not see the point anymore. I guess it will be resolved eventually as this appears to be a pretty fundamental functionality. To reproduce just drag VisualElement onto the item-Template Field, save uxml, and observe what is saved (in Unity 2023.3.b05)
     
  14. OuterGazer

    OuterGazer

    Joined:
    Jan 13, 2021
    Posts:
    12
    Hi and thank you for this response.

    I'm creating a custom editor for a mission system and it implements a Listview for objectives (the class Mission holds a List<Objective>). Objectives are its own ScriptableObject that contains a string, an int and an enum. It has its own custom editor script and UXML (binding paths are set to each of the properties in the UI Builder). In the general Mission custom editor (where the ListView is), I implement the previously mentioned UXML as itemTemplate in the ListView Control and set the binding mode to autoassign. I followed those 3 steps.

    Thing is, the custom editor for the objectives itself works properly when I create one, the properties are shown and are in sync. The binding between the objective list property in my mission class and the ListView "works" in that it displays the correct number of items when I add an objective. The template is added and its shown in the inspector just how it is built in the UXML, however, its fields are not bound and reset upon changing focus in the inspector. When I change properties in the ScriptableObjects they are not changed in the elements shown in the Mission custom inspector and viceversa.

    Do I need to do something else?

    As something of note, Objective is an abstract class and what I add to the list in Mission is a child that derive from it.

    EDIT: Now I have seen that the actual count of the ListView is 0 even though the binded list has elements and the template appears where the ListView control is. I'm adding elements to the list by calling a class method through target, could that be where the problem lies?
     
    Last edited: Feb 14, 2024
  15. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hi @OuterGazer

    Somehow, I missed your post, my apologies.

    From you description, I'm not sure if you are using the runtime bindings or the editor bindings. The editor bindings is a specialized binding type that understands about undo/redo,
    SerializedProperty
    , etc. The runtime bindings is the more general system, which is not aware of the editor concepts.

    Since you are dealing with custom editors, most likely, you should be using the editor bindings. If so, can you post your question in https://forum.unity.com/forums/ui-toolkit.178/ ?
     
  16. hawaiian_lasagne

    hawaiian_lasagne

    Joined:
    May 15, 2013
    Posts:
    127
    I'm so confused, why can't the listview just use Data Source Path in UI Builder? Instead of manually editing the xml and adding the <Binding> thingy?
     
    Last edited: Apr 12, 2024
  17. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    That is because we support multiple types of bindings, including user-defined binding types. We also support having multiple bindings on the same element targeting different properties, including style properties.
     
  18. hawaiian_lasagne

    hawaiian_lasagne

    Joined:
    May 15, 2013
    Posts:
    127
    I understand that, but it feels as if this should be the basic default workflow that everyone expects:
    - Select List View
    - Select Data Source (scriptable object)
    - Select Data source path (filtered by list/array property type)
    - Assign uxml item template
    - Done
    If a user wants to provide additional bindings, or provide their own custom binding then they could drop down to uxml at that point?
     
  19. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    Hi @hawaiian_lasagne,

    What you are suggesting is roughly how the editor bindings currently work (minus the data source part, since it's implicit /set by the system in that case). One feedback we've often received from that way of doing is to be able to bind to other properties than to
    value
    .

    The editor systems has several drawbacks:
    • You can only bind again
      value
      of an element implementing the
      INotifyValueChanged
      interface. In some cases, we can allow some other properties, but these are more or less hardcoded.
    • You cannot bind against any other property.
    • It can only bind against serialized data, which means limited data types.
    Both systems supports relative and absolute binding paths and in both cases, if a binding was automatically created between
    value
    and the data source property, you would need to provide absolute paths for any other bindings you would want to use. This is not an issue with the editor bindings, as you cannot have multiple bindings anyway.

    The way I see it, we have one system where "one way fits all" and is not extensible and one system where you have more control over what is happening and is extensible. You can build something in the new system that would emulate the old system (because that is precisely what we've done to the old system: it's re-written in the new system as a custom binding), but the other way around is not possible.

    To get the workflow you want, you could derive from
    ListView
    , override the
    makeItem
    and manually clone the
    itemTemplate
    , fetch the elements that have set the
    dataSourcePath
    and add a binding object targeting the proper property (
    value
    for
    BaseField<T>
    ,
    text
    for
    Label
    , etc.)

    Hope this helps!
     
  20. hawaiian_lasagne

    hawaiian_lasagne

    Joined:
    May 15, 2013
    Posts:
    127
    Thanks for the reply @martinpa_unity

    Ultimately I was looking for a way to bind the list view to a List<> field on a scriptable object, without any custom code (inheriting list view, manual modifying uxml)

    The new binding system seems like it is almost there, but not quite.

    And I think the old(?) editor binding system doesn't support assigning a scriptable object to preview the behaviour in uibuilder - please correct me if I'm wrong.

    I will stick with modifying the uxml for now as it's working pretty nicely and gives a preview in uibuilder.

    This is for a custom editor and not runtime, so the only thing I'm worried about is if I continue down this path, will I loose undo/redo support? Ideally I'm looking for a solution where I can assign a scriptable object, preview in uibuilder, and have undo/redo support.

    Any further advice you have is much appreciated
     
  21. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    485
    The old binding system is using the new system. Instead of being a
    DataBinding
    , it derives from
    CustomBinding
    and handles all the expected editor operations, including undo/redo.

    The editor binding system should work anywhere within the editor, it's mostly the UI Builder that doesn't allow to set an object for preview out-of-the-box.

    However, for
    ScriptableObject
    , you can make a custom element that would give you this. For this, you could:
    • Use the new Uxml Serialization feature to have a
      ScriptableObject
      property as well as a
      bindingPath
      property and, under the hood, assign and bind a
      PropertyField
      .
    • You could also use a
      CustomBinding
      and do the same.
    • If you don't want to use a
      PropertyField
      and bind to your existing hierarchy, you can also create a custom element with a
      ScriptableObject
      property and call
      Bind
      /
      Unbind
      .
    It takes a little bit of elbow grease, but previewing in the UI Builder should be possible with both systems.

    That is correct, the runtime binding system is not aware of the undo/redo feature, because it is editor-only. That being said, you should be able to preview the result in the UI Builder using one of the three options I presented.

    Hope this helps!