Runtime data binding through custom class with value and bindings

Hello. Could you please help me figure out what I’m doing wrong?

Previously, in my PressureData class, I defined Speed like this:

[SerializeField] private int speed;
[CreateProperty] public int Speed
{
    get => speed;
    set
    {
        speed = value;
        speedBinding?.MarkDirty();
    }
}
private DataBinding speedBinding;
public DataBinding SpeedBinding => speedBinding ??= new DataBinding {
    dataSourcePath = new PropertyPath(nameof(Speed)),
    updateTrigger = BindingUpdateTrigger.OnSourceChanged,
    bindingMode = BindingMode.ToTarget,
};

And the binding in the necessary VisualElement worked like this:

dataSource = pressureData = _pressureData;
SetBinding(nameof(Speed), pressureData.SpeedBinding);

Everything worked in the binding, and the interface updated correctly.

Now, I’ve added a class:

[Serializable]
public class BindableProperty<T>
{
    [SerializeField] public T value;
    [SerializeField] private BindingUpdateTrigger updateTrigger;
    [SerializeField] private BindingMode bindingMode;

    public string PropertyName { get; }

    [CreateProperty] public T Value
    {
        get => value;
        set
        {
            this.value = value;
            binding?.MarkDirty();
        }
    }

    private DataBinding binding;
    public DataBinding Binding => binding ??= new DataBinding
    {
        dataSourcePath = new PropertyPath($"{PropertyName}.Value"),
        updateTrigger = updateTrigger,
        bindingMode = bindingMode,
    };

    public BindableProperty(T initialValue, string propertyName, BindingUpdateTrigger updateTrigger = BindingUpdateTrigger.WhenDirty, BindingMode bindingMode = BindingMode.ToTarget)
    {
        value = initialValue;
        PropertyName = propertyName;
        this.updateTrigger = updateTrigger;
        this.bindingMode = bindingMode;
    }
}

And in PressureData, I defined it like this:

[CreateProperty] public BindableProperty<int> Speed = new BindableProperty<int>(0, "Speed", BindingUpdateTrigger.OnSourceChanged);

The binding is now:

SetBinding(nameof(Speed), pressureData.Speed.Binding);

But now the interface does not update as expected. What am I doing wrong?

Which version of the editor are you using? I’ve tried it locally and it seemed to work fine. I’ve used an IntegerField, binding to the value property using:

field.SetBinding("value", dataSource.Speed.Binding);

I’m using Unity 6000.0.25.

In my example, Speed in the custom element is an int property, which then gets assigned to a Label in the set. I pass pressureData as the dataSource to this custom element, where Speed and other bindable properties are stored.

Could you show me how you set everything up to make it work?

Sure, I’ve rigged it using this MonoBehaviour:

using UnityEngine;
using UnityEngine.UIElements;

public class MyBehaviour : MonoBehaviour
{
    private void OnEnable()
    {
        var document = GetComponent<UIDocument>();
        if (document.rootVisualElement == null)
            return;

        var root = document.rootVisualElement;
        var field = root.Q<IntegerField>();

        var dataSource = (DataSource)field.dataSource;
        field.SetBinding("value", dataSource.Speed.Binding);
    }
}

And this as the data source:

using Unity.Properties;
using UnityEngine;
using UnityEngine.UIElements;

[CreateAssetMenu(fileName = "DataSource", menuName = "Data Source", order = 0)]
public class DataSource : ScriptableObject
{
    [CreateProperty]
    public BindableProperty<int> Speed = new BindableProperty<int>(0, nameof(Speed), BindingUpdateTrigger.OnSourceChanged);
}

And the UXML file:

<engine:UXML
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:engine="UnityEngine.UIElements"
        noNamespaceSchemaLocation="../UIElementsSchema/UIElements.xsd">
    <engine:IntegerField
            label="Integer Field"
            value="42"
            data-source="project://database/Assets/DataSource.asset?fileID=11400000&amp;guid=6fa9dca141170da4296a2915482a6352&amp;type=2#DataSource" />
</engine:UXML>

Let me send you the complete setup shortly; I’ll send it to you in a private message.

Hello. I’ve sent you my entire setup in a private message.