Introducing the Runtime Bindings API in Unity 2023.2

Out of the three examples that I gave, I would almost always use the first one, as I like to have reusable pieces of UI that behave the same way in different contexts. Those kind of elements are mostly what I was using before the bindings feature and enabling the feature on it only needed two one-line changes (adding the CreateProperty attribute and notifying of the change).

This would be the case for the majority of custom elements. Adding bindings can be done completely on the uxml side and most data binding can be added through code using a single call per property.

However, Iā€™ve seen enough users that want to create and maintain close to 0 custom elements that included the two additional examples. I would recommend to use the first approach, for the stated reasons.

This is still completely supported. Querying visual elements, manually transferring your data and registering value changed callbacks whenever the source needs to be updated is a valid way to go.

1 Like

@Thygrrr It seems very similar to WPF, which is rather verbose when you are coding it but usually is hidden behind some kind of code-generation via xml tags. Not that it appears that is how it will work here. Itā€™s a step up from, well, absolutely nothing, which is what we have right now.

All that being said, I donā€™t disagree wtih you either. Iā€™ve written my own databinding in the past for Unity projects via runtime code emitting and when it can be boiled down to just a couple function calls to binding and unbind itā€™s hard to want to go back. Static analysis would be nice but at this point Iā€™d be happy for something that works and comes out within the effective lifetime of UIToolkit.

While Iā€™m not crazy about it Iā€™m used to writting code like that enough that I donā€™t mind. But honestly, my biggest disappointment is that itā€™s effectively still a year out at best before it can be used in any kind of real project.

You donā€™t have to wait for Unity 2023. You can take a look at the UnityMvvmToolkit package to get data-binding support.

1 Like

Oh, donā€™t worry. I wasnā€™t waiting :wink: Even if I was that patient Iā€™ve been at this for too long to know how often these things donā€™t pan out.

Thanks for the link though! Looks much more fully-featured than my own solution.

@martinpa_unity For me the basic example in the first message isnā€™t working in latest Unity 2023.2.0b4. I have created a GameObject in the scene with UIDocument and the following TestScript on it.

However when I start I see only the integer field be 0 and no name visible.

Is it possible to get some example projects up on GitHub on how to set this up?
Thank you for the help

Hi @BlackSpider !
Apologies, I seemingly had copied the wrong example while writing the initial message. The DataSource should have been:

public class MyDataSource
{
    [CreateProperty]
    public string Name { get; set; }
    [CreateProperty]
    public int Level { get; set; }
}

There was also another typo where the example was using nameLabel instead of levelField to display the level.

Iā€™ve updated the post. Thanks for pointing it out!

1 Like

@martinpa_unity Thank you that made it work :smile: really cool. Still would love to see some example projects that maybe show like more complex examples. (Like the ListView)

@BlackSpider Yes, samples will be coming. Stay tuned! :slight_smile:

1 Like

We must just be patient, theyā€™re still busy typing up all that boilerplate for those samples, this canā€™t be written overnight.

Iā€™ll provide a sample implementation later hopefully when I have a few free hours. Admittedly I got ref fields from .net 8 mixed up, but just POCOs will do the job.

I still think the data source should be passed into a method on the visual element, and that should then construct and return the binding. Not the other way around.

I think the implementation works great when you use the UI Builder. Did try to write the least lines of C# I could. And for me the ListView which I got working quite easily with only a few lines of code.

I assume with some custom VisualElements you can even hide all this and do something like, SetProfileViews(views);

9228072--1288791--upload_2023-8-16_23-54-44.png

1 Like

Hi everyone,
Is it possible binding data to USS selectorā€™s properties or custom properties?
Such as: I created transition animation with USS selectors and I want to set this animationsā€™ parameters from ā€œbinding data classā€.

Hi @Mj-Kkaya !

If you mean to change the values directly in the style sheet instance, then that is not possible. Style sheet selectors are cached and changing their values on the fly would most likely mean a lot of invalidation and it would force a styling pass on any visual hierarchy using that style sheet.

However, there are ways to do this without changing the style sheet instance:

  • For the style properties, you could bind to the inline styles using the ā€œstyleā€ prefix as the path (i.e. ā€œstyle.backgroundColorā€, ā€œstyle.transitionPropertyā€, etc.) This will let you parametrize your animation for a given element.
  • For the custom style properties, usually, you need to query them in code and deal with them manually, as most custom properties wonā€™t do anything by default. Note that transitions on custom properties is not currently supported. You can instrument your own custom properties with [CreateProperty] and you should be able to use the binding system on them.
  • If your animationsā€™ parameters are part of a known ahead of time set (i.e. duration will either be 0ms, 250ms or 500ms), you can also define those in the style sheet and use a binding to add/remove uss classes. When trying to bind to multiple style properties at the same time, this method is probably the best.

Hope this helps!

1 Like

Hi @martinpa_unity , thanks for your response.

I will consider this!

Actually Iā€™m using add/remove uss classes with my transition animation. And I avoid to use inline-style for performance considerations.
But it seems, I have to use inline-style for transition parameters.

There is my test transition parameters.
For this example: If I want to control the ā€œtransition-durationā€ parameter with ā€œBinding Dataā€, I have to use ā€œbinding dataā€ in ā€œinline styleā€.

.fade-effect__transition {
    transition-duration: 1.5s, 1.5s;
    transition-timing-function: ease-in-sine, ease-in-sine;
    transition-property: scale, background-color;
}

.fade-effect__transition--fade-in {
    background-color: rgb(255, 0, 0);
    scale: 0.5 0.5;
}

.fade-effect__transition--fade-out {
    background-color: rgba(255, 0, 0, 0);
    scale: 1 1;
}

If you need to pass in dynamic values, inline style will be the way to go.

1 Like

Hi Martinpa_unity

I can see youā€™re working hard on all thisā€¦you mentioned ā€˜samples will be coming. Stay tuned!ā€™

Any ETA on thisā€¦

Hi @GameDev273 , Iā€™ll check on the status of this. Unfortunately, the last weeks have been quite bumpy to say the least and itā€™s vacation season at the moment.

Could someone provide an example of event binding, like a button click?

martinpa_unity - Ref ETA ā€¦thanks

seika850113
There is an example in the 2023.2 documentationā€¦

Although when I followed it the uxml file generated wasnā€™t quite the sameā€¦so at step 9 of
Bind the Label to the data source
where it saysā€¦

ā€˜Save and close UI Builder. Your ExampleObject.uxml file looks like the following content:ā€™

It didnā€™tā€¦ so I duplicated the ExampleObject.uxml file and copied in the example shown in step 9.

Also you need the 2023.2.a18 alpha release of Unity

Hi @seika850113_1 , event bindings are not supported out of the box, but here is a very quick and dirty example of how it can be achieved using a custom binding:

using System;
using System.Collections.Generic;
using Unity.Properties;
using UnityEngine.UIElements;

namespace Example
{
    [UxmlObject]
    public partial class ActionBinding : CustomBinding
    {
        // Caching the delegate used for cleanup purposes.
        private readonly Dictionary<VisualElement, Action> m_CachedDelegates = new ();

        // Only tracking the registration when the data source context changes. This wouldn't
        // pick-up if the targeted `action` changed on the data source.
        protected override void OnDataSourceChanged(in DataSourceContextChanged context)
        {
            if (context.targetElement is not Button button)
                return;

            // Clean previous callbacks
            if (m_CachedDelegates.TryGetValue(button, out var action))
            {
                button.clicked -= action;
                m_CachedDelegates.Remove(button);
            }

            // Extract the `Action` from the hierarchy and register it.
            var source = context.newContext.dataSource;
            var path = context.newContext.dataSourcePath;

            if (null == source || !PropertyContainer.TryGetValue(ref source, in path, out action))
                return;
           
            button.clicked += action;
            m_CachedDelegates.Add(button, action);
        }
    }
}

Hope this helps!

1 Like

Hello @martinpa_unity ,
thanks for the example. Iā€™m trying to make it work on a test project, but I canā€™t pass dataSourcePath to it. What I have in uxml is:

<ui:Button text="Press me">
<Bindings>
<ActionBinding property="whatever" data-source-path="_printStuff"/>
</Bindings>
</ui:Button>

When I break at line 32 of your example (PropertyContainer.TryGetValue(ref source, in path, out action)) I see, that path is empty (isEmpty = true, length = 0). Not sure if CustomBinding has data-source-path attribute exposed, but I donā€™t see how else I would pass the path to the binding. Do you have an idea of what Iā€™m doing wrong?
On the side note, it feels that the property attribute of a binding shouldnā€™t be mandatory, as in this case and some other examples of custom bindings released so far, it is not used.

Edit:
I was able to add my own dataSourcePath UXMLAttribute and set that up. I tried it before but got confused by the transformation from dataSourcePath in code to data-source-path in UXML. However, it seems a bit strange that Iā€™m using the context only for data source, but not the dataSourcePath:

// Extract the `Action` from the hierarchy and register it.
var source = context.newContext.dataSource;
//var path = context.newContext.dataSourcePath;
var path = PropertyPath.FromName(dataSourcePath);