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 How do you use new UIToolkit Binding system?

Discussion in '2023.2 Beta' started by MechaWolf99, Jun 9, 2023.

  1. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    I saw in the latest release (#18) that there is something about binding. It seems like it is the runtime binding that has been talked about for a while. So I checked it out, and the APIs look cool, but I couldn't for the life of me figure out how to use it.
    I tried to set the data source path to be a field and a property, and in C# setting the data source to a component, and also a POCO. But got nothing. Of course read what docs I could find. So before I dove too deep in to the source to figure it out. I thought I would ask here first, is it a runtime binding system? Does it work at runtime? And if so, is there some sort of code snippet example of how to use it in the most basic way?

    Or is it best to just wait for now before trying it out?

    Thanks! :)
     
    stevphie123 and redwyre like this.
  2. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    Hi @MechaWolf99,

    We indeed shipped the runtime binding feature and are working on a general announcement. This should be available soon. Look for it in the UI Toolkit forum. :)

    The basic idea behind the new binding system is that you get to configure the sources and the binding themselves. Because you can now bind to multiple properties of a given element, you need to create binding objects manually. For a simple example:

    Code (CSharp):
    1.  
    2. // Some data somewhere
    3. public class MyBehaviour : MonoBehaviour
    4. {
    5.     public float value;
    6. }
    7.  
    8. // Elsewhere, set your mono behaviour as a data source
    9. var field = new FloatField
    10. {
    11.     dataSource = gameObject.GetComponent<MyBehaviour>();
    12. };
    13.  
    14. // Create a binding between the value of the field and the value of your behaviour
    15. field.SetBinding(nameof(FloatField.value), new DataBinding { dataSourcePath = PropertyPath.FromName(nameof(MyBehaviour.value)) });
    16.  
    Note that the registration of binding objects and their update is tied to the update mechanism of the UI panel, so the updated value will not be available immediately, but will be ready before rendering.

    There are a lot of things to say about the new system, so I'll keep it to the simple example I've provided, as it should get you started on using it. We'll talk more about the capabilities it provides in the proper announcement.

    Hope this helps in the meantime.
    Thanks!
     
  3. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Thanks for the reply! It is an interesting setup. Would it be better to wait for the official announcement thread to share feedback so it is all in one place?
     
    mariandev likes this.
  4. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    Why not both? :)
     
  5. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Works for me! A couple of things I noticed.
    1. If you have a element defined in UXML, the data-source-path that is set there looks like it takes precedence over SetBinding.
    2. I tried setting the .dataSource property in C# and having a UXML element with the data-source-path set, but it doesn't seem to bind. maybe I am doing something wrong.
    3. It appears that using SetBinding, the DataBinding.dataSourcepath only supports fields. This is a pretty big issue that it doesn't support C# properties, considering that means you can't get raise events when values changes and having public fields is against C# guidelines. Please let me know if I am wrong and it does support C# properties!
    4. Using SetBindings, and specifying the value on the float field feels really weird. I mean it makes sense, so you can support more advanced use cases. But feels really weird.
    5. I would be interested in seeing a more complete sample later on, because right now it feels like setting up binding has a lot of boilerplate involved, especially when you consider having to do it for a multitude of properties.

    That is all I have at the moment. Will probably have more once the thread is posted and I can read more complete info on it and maybe some more examples :)
     
  6. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    A couple of things here. There is a
    dataSource
    and
    dataSourcePath
    on both
    VisualElement
    and
    DataBinding
    .

    The
    dataSource
    and
    dataSourcePath
    on the binding object is local to that specific binding object and will not have any effect on other binding objects and/or element. The
    dataSource
    and
    dataSourcePath
    on the
    VisualElement
    will affect all the bindings of that element and child element, until they are overridden.

    In the end, this is how we resolve which data source and path to use: starting from the binding object, we combine the
    dataSourcePath
    together until we find a
    dataSource
    that is set. if the
    dataSource
    is set on the binding itself, we use that one. Otherwise, we will look for the
    dataSource
    and
    dataSourcePath
    on the owning element and its ancestor, until we find a
    dataSource
    that is set.

    This way, you could have a global data source that is set at the root your visual tree and use the
    dataSourcePath
    as filters in your hierarchy (i.e. under this root, the elements show only have access to ${datasource}@player.inventory), while still retaining the ability to have specific bindings that link to a different data source/path.

    So, if you set the
    dataSourcePath
    of an element in uxml, it will be set on the element in code and the binding object should not repeat it.

    Did you add a binding from code or from uxml?

    From uxml, it looks like this:

    Code (CSharp):
    1. <ui:VisualElement>
    2.     <Bindings>
    3.         <ui:DataBinding property=style.backgroundColor data-source-path="myColor/>
    4.    </Bindings>
    5. </ui:VisualElement>
    Good news, it does support c# properties, but you need to instrument them to make them available. Here is an exemple:
    Code (CSharp):
    1. // To support Unity serialization
    2. [SerializeField]
    3. // For ease of use, we automatically create a property when [SerializeField] is used, but you can opt-out using this if you do not want users to be able to bind against m_Value.
    4. [DontCreateProperty]
    5. private float m_Value;
    6.  
    7. // Expose the property to the binding system
    8. [CreateProperty]
    9. public float value
    10. {
    11.     get => m_Value;
    12.     set => m_Value = value;
    13. }
    At the moment, you need to use a string of the
    nameof
    as I've done in the sample, however, the preferred way will be to do something like this:
    fieldField.SetBinding(FloatField.valueProperty, new DataBinding { ... });


    The "properties" are there, but remained internal for now. We will expose them at a later time.

    The
    dataSource
    affects the current element and its children, until it's overridden. What this means is that you do not need to setup the data source on every element. Typically, you would set it on a root where multiple bindings in the sub-hierarchy can use it.

    Ideally, the preferred way to setup the bindings would be to use the UI Builder to create the bindings directly in uxml. If your data source is a scriptable object, you can also that in your uxml file, which would allow you to use bindings with writing any code for it.

    In a lot of cases, what I do is set the bindings in uxml and then assign the data source of my root element from code.

    Hope this helps!
     
    Last edited: Jun 9, 2023
  7. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Hey, thanks for the detailed answers! :D

    Yeah,I was figuring that is how it works. Basically the same as the SerializedObject binding system.

    This is the UXML
    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    2.     <ui:FloatField label="Foo" value="42.2" data-source-path="_foo" />
    3. </ui:UXML>
    And the C#
    Code (CSharp):
    1. public class UITest : MonoBehaviour
    2. {
    3.     [SerializeField] private float _foo;
    4.  
    5.     private void Start()
    6.     {
    7.         var doc = GetComponent<UIDocument>();
    8.         doc.rootVisualElement.dataSource = this; // With only this line, and the UXML, nothing happens.
    9.         // If the `data-source-path` is set in UXML, this line does nothing either.
    10.         doc.rootVisualElement.Q<FloatField>().SetBinding(nameof(FloatField.value),
    11.              new DataBinding() { dataSourcePath = new PropertyPath("_foo") });
    12.     }
    13. }
    Oooh right, I remember reading the in the Property docs about that! I did think it was strange that C# Properties were not supported. Sorry! Though I do find it strange that it works this way. I would expect that by default properties and public fields are exposed, but not private fields. Feels unintuitive to me considering the access level of the declarations.

    I will be honesty, this is really weird to me. I mean, it is a great idea. Being able to set it out without code. But requires you to have a single UXML per ScriptableObject. I have almost never seen a ScriptableObject class be made that was single instance use. Maybe like settings or something. I would love to hear the thinking behind this, because unless I am not understanding how it works. This feels like a pretty niche feature that just kind of bloats it a bit.

    If you wanted to keep the idea of being able to set it up without using any code. I feel like using the
    data-source-type
    in the UXML would make more sense. And have the the UIDocument read it and expose a list of Object references that you could assign and have it handle the binding that way.
    What is
    data-source-type
    used for anyway?

    Thanks again for the answers! It is indeed helpful! :)
     
    martinpa_unity likes this.
  8. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    So, form your snippets, you are defining both the
    dataSourcePath
    on the element and on the binding. This will result in the resolved path being
    _foo._foo
    , this is one
    _foo 
    too many.

    What I meant is that the preferred way to avoid the code boiler plate would be to setup the binding object in uxml directly. That way, you can instantiate your uxml file, set the data source without having to add bindings manually.

    If your data happens to be a scriptable object, you can also reference it from uxml as well to set it as a data source. This is not a mandatory step. This is also a limitation of the uxml asset, because it can only reference other assets and not arbitrary data. From code, you can set arbitrary data as a data source.

    Starting from your example, you could do something like this:
    UXML:
    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    2.     <ui:FloatField label="Foo" value="42.2">
    3.         <Bindings>
    4.             <ui:DataBinding property="value" data-source-path="_foo"/>
    5.         </Bindings>
    6.     </ui:FloatField>
    7. </ui:UXML>
    C#:
    Code (CSharp):
    1. public class UITest : MonoBehaviour
    2. {
    3.     [SerializeField] private float _foo;
    4.     private void Start()
    5.     {
    6.         var doc = GetComponent<UIDocument>();
    7.         doc.rootVisualElement.dataSource = this; // With only this line, and the UXML, nothing happens.
    8.     }
    9. }
    The
    data-source-type
    is there for the UI Builder purposes, it allows the use of auto-complete when you know which type you intend to use at runtime, but can't link in your uxml.
     
    Last edited: Jun 9, 2023
  9. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Interesting to know that using
    SetBinding
    is additive. Guess it makes sense.

    I see, I think I am starting to get it.
    However, when I copy pasted the UXMLI get this error. Not sure if that is me understanding something, or maybe something didn't get pushed?
     
  10. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    Oh, sorry, it's just
    <Bindings>
    and not
    <ui:Bindings>
    .
     
  11. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    All good, that fixed it! Though it still isn't binding. This is what I have now but the FloatField isn't binding to the foo field in my component.
    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    2.     <ui:FloatField label="Foo" value="42.2">
    3.         <Bindings>
    4.             <ui:DataBinding property="value" dataSourcePath="_foo"/>
    5.         </Bindings>
    6.     </ui:FloatField>
    7. </ui:UXML>
    Code (CSharp):
    1. public class UITest : MonoBehaviour
    2. {
    3.     [SerializeField] private float _foo;
    4.  
    5.     private void Start()
    6.     {
    7.         var doc = GetComponent<UIDocument>();
    8.         doc.rootVisualElement.dataSource = this;
    9.     }
    10. }
     
  12. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    Did you go in play mode?
     
  13. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Yup
     
  14. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    I just tried your sample on my side and it's working as expected.

    Can you package your example, so that I may have a look at it?
    Thanks!
     
  15. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Sure, here you are. Just drop it in a project and open the sample scene and hit play. I'm sure it is just some small thing I missed somewhere. I appreciate you taking a look!
     

    Attached Files:

  16. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    Oh, I'm very sorry, while rewritting your sample here, I used the C# name for the
    dataSourcePath
    property. Those get converted to
    data-source-path
    for uxml. I have updated the snippets.


    Friday brain fart. :)
     
  17. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Ooh, haha. That was even something I noticed and was going to check if that was what happened, but forgot to. Thank you for your help with this and all the info! Overall the system seems pretty nice and much more flexible than I was expecting! I am looking forward to trying it out in an actual project later on :)
     
    martinpa_unity likes this.
  18. Onigiri

    Onigiri

    Joined:
    Aug 10, 2014
    Posts:
    490
    ETA for the announcement post?
     
  19. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    @Onigiri We're hoping for end of this week or early next week.
     
  20. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    The announcement has finally been made here.
     
    PaulMDev, Ruchir and MousePods like this.
  21. SebMaire

    SebMaire

    Joined:
    Feb 9, 2021
    Posts:
    12
    Hi there !

    I am facing the exact same case and using the unitypackage provided by @Onigiri with the fix for the data-source-path. For me nothing is linked, updating the data does not refresh the UI and changing the field does not change the data.

    I am using unity 2023.2.0a22, I have the exacte same problem with unity 2023.2.0b4.

    Do you have any clue on why this does not work ?
     
  22. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    I tried it again and it works for me. The bindings work in play mode.

    And if I tag the
    UITest
    class with
    [ExecuteAlways]
    , it also binds outside of play mode.

    Is there any errors while importing the package?
     
  23. SebMaire

    SebMaire

    Joined:
    Feb 9, 2021
    Posts:
    12
    Ok to exclude any issue on my side I tried the unitypackage in a new project with data-source-path fixed, and it worked, the only difference I had was that the [SerializeField] private float _foo was below the start function. Putting it on top magically made it work.
    Trying to reproduce this on the test project worked normally so I rolled back and put my field in original position, and it still worked.
    Looks like a refresh issue with domain reload or something
    Thanks for answering ;)
     
    martinpa_unity likes this.
  24. Knedlo

    Knedlo

    Joined:
    Oct 15, 2012
    Posts:
    54
    Hello, just started to explore the bindings and I'm experiencing the same problem as SebMaire. I've made a project from 3D template in 2023.2.0b9 and recreated the files as per example above. Strung it together, hit play and nothing. I was able to reproduce the example from the binding documentation that creates bindings from code. No errors, no warnings, just not being bound or updated. I'm attaching the project (it's completely bare bones).

    Alright, my bad. I copied the wrong snippet. The problem was the mentioned dataSourcePath instead of data-source-path in uxml. After reading that the example was updated, I haven't even think to check it, but there are two version of it and only one is correct :)
     

    Attached Files:

    Last edited: Sep 24, 2023
  25. martinpa_unity

    martinpa_unity

    Unity Technologies

    Joined:
    Oct 18, 2017
    Posts:
    484
    Hi @Knedlo, from what I could see in your sample, it should be the same resolution as described here.

    Hope this helps!