Binding to instance

I recently updated to Unity 6 and wanted to migrate my current localization binding system to built-in Localization-to-UI Toolkit binding, however I stumbled on a problem.
Is there a way to have a localization binding on an Instance of a control?
I.e. I have a button template with a text inside a button. It’s reused quite a few times over the project, and I want to change the text on the button in different places where it’s used.
Is there any way to have a different binding for a different instance of the button template, or it’s not possible?

Thanks.

Templates act like blackbox that can only be instantiated. Until you unpack them or modify it at the runtime, you can’t bind anything under the TemplateContainer.

Instead, you should use the default controls (i.e. for Button) or create your own Control that inherits from the VisualElement you want to enhance and, then, expose bindings you want to translate.

This makes absolutely no sense.
I have pretty complicated controls with icons and shadows inside them, duplicating that all over the place in a project is just asking for hell when something needs to be changed. Sure, you can include USS file for each of the used control each time you use a “template” but this makes absolutely no sense because if something changes in a control structure (i.e. adding a visual element or removing it), you have to go around all over you project to change everything. There is an attribute override system for a reason. Bindings should also be overridable. Otherwise, all this localization binding system is useless.

Is there a way to have a localization binding on an Instance of a control?

Do you mean an instance of a UXML/VisualTreeAsset or an instance of a control here?

If you meant an instance of a control, then as long as it’s properly instrumented, you should be able to add bindings to it. On the control side, that means tagging your data with the [CreateProperty] attribute and reporting changes (which you can see here).

If you meant an instance of a UXML/VisualTreeAsset, then at the moment, you need to do it through code. The attribute override system works for the attribute, but it doesn’t work for the UXNL objects just yet. It’s a known limitation that we will address.

However, you could use attribute to drive the values of the bindings and override those.

1 Like

I was referencing to the instance of UXML.
Can you please evaluate on the last paragraph about attributes to drive values.

You can use uxml attributes, which are overridable in a template to drive the values of the binding. For example, you can set a binding from the AttachToPanelEvent and use the control’s attribute the set the relevant binding attributes. It’s a workaround for sure.

Ah, okay. Understood. That’s what I’m currently using for my localization binding system.

@martinpa_unity

I am banging my head against the wall here :see_no_evil: . But I feel the solution is really simpel. I am trying to bind a LocalizedString to a label. But the way I am currently doing it doesn’t work because I see the whole ToString() instead of the expected value.

I define the LocalizedStrings inside of a ScriptableObject like this.

[CreateAssetMenu(fileName = nameof(Product))]
public class Product : ScriptableObject
{
    [SerializeField] private LocalizedString ProductName;
    [SerializeField] private LocalizedString ProductCategory;
    [SerializeField] private float ProductPrice;
    [SerializeField] private Sprite Image;
}

Now in the UXML I set the DataType as Product and link the image to the Image property and that works as expected.

However when I do this for the LocalizedString it shows the label as a ToString from the LocalizedString. Instead of the expected localized text.

 <engine:Label name="text-category" class="text-muted-foreground">
    <Bindings>
        <engine:DataBinding property="text" data-source-path="ProductCategory" binding-mode="ToTarget" />
    </Bindings>
</engine:Label>

{98E47FE4-8065-495B-BD41-5ECE4D215B0B}

{62CD49FE-AE1F-4582-A5BB-C88C7FAEBD1D}

I also tried setting it from an extended VisualElement class like so. But that also doesn’t work.

C#

[UxmlElement]
public partial class ProductElement : VisualElement
{
    [UxmlAttribute]
    [CreateProperty]
    public string ProductCategory;

    private Label labelName;
    private Label labelPrice;
    private Label labelCategory;
    private VisualElement image;

    public ProductElement()
    {
        var tree = Addressables.LoadAssetAsync<VisualTreeAsset>("Product").WaitForCompletion();
        tree.CloneTree(this);

        labelName = this.Q<Label>("text-name");
        labelCategory = this.Q<Label>("text-category");
        labelPrice = this.Q<Label>("text-price");
        image = this.Q<VisualElement>("image");

        Bind();
    }

    private void Bind()
    {
        labelCategory.SetBinding("text", new DataBinding { bindingMode = BindingMode.ToTarget, dataSourcePath = new PropertyPath(nameof(ProductCategory)) });
    }
}

UXML

<ProductElement data-source="project://database/Assets/Data/Products/Test.asset?fileID=11400000&amp;guid=04e8c041479cfc34499388d622d8bae0&amp;type=2#Test" class="h-80 w-52">
    <Bindings>
        <engine:DataBinding property="ProductImage" data-source-path="Image" binding-mode="ToTarget" />
        <engine:DataBinding property="ProductType" data-source-path="ProductCategory" binding-mode="ToTarget" />
        <engine:DataBinding property="ProductPrice" data-source-path="ProductPrice" binding-mode="ToTarget" />
        <engine:DataBinding property="ProductName" data-source-path="ProductName" binding-mode="ToTarget" update-trigger="EveryUpdate" />
        <engine:DataBinding property="ProductCategory" data-source-path="ProductCategory" binding-mode="ToTarget" update-trigger="EveryUpdate" />
    </Bindings>
</ProductElement>

How can I make it so that the Localized text shows up when binding to a LocalizedString that is referenced from a ScriptableObject. Do I need to do that through C#? It would be great if you can show some examples.

LocalizedString has its own binding support, you may find it simpler to use that UI Toolkit (Unity 6+) | Localization | 1.5.2

If you do need this approach to work then you have 2 options.

  1. Add a converter to convert from LocalizedString to string. The converter will need to call GetLocalizedString instead of ToString.
  2. Wrap the LocalizedString with a string property.
[CreateAssetMenu(fileName = nameof(Product))]
public class Product : ScriptableObject
{
    [SerializeField] private LocalizedString ProductName;
    [SerializeField] private LocalizedString ProductCategory;
    [SerializeField] private float ProductPrice;
    [SerializeField] private Sprite Image;

    public string ProductNameString => ProductName.GetLocalizedString();
}

Thank you for the reply @karl_jones

I use the LocalizedString binding already for all my “static” UI. But I can’t use that for scriptableobjects correct? :thinking:

If I understand correctly when I use the ProductNameString example it will not update when I switch to a different language? And that is also the case for a custom Converter? This also means that Variables that are inside of the localization can’t be updated like I would do with the LocalizedString class.

It would be cool to have a .value maybe available on the LocalizedString which you can bind to.

You need to inform the binding system when a bound value has changed so it can re-bind. One way to do this here is for your Product class to implement the interface INotifyBindablePropertyChanged
Invoke the propertyChanged event when the language changes.

@karl_jones

Did some research into the suggested solutions. The LocalizedString convert seems to be the best solution for now. It even updates when I switch language.

The Property ProductNameString => didn’t work, I assume it is because it is a get only.

It would be great if you can just link to the LocalizedString property itself without a converter. Or have a .value in localizedstring that does sort of the same.

prod.value
{FE03F333-E15E-4C94-AB5E-70E364E6FDC6}

Converter:
{7C32A4AD-A40E-4794-9657-D04F16BCC45D}

Property ProductNameString

using UnityEngine;
using UnityEngine.Localization;

[CreateAssetMenu(fileName = nameof(Product))]
public class Product : ScriptableObject
{
    public string ProductNameString => prod.GetLocalizedString();

    [SerializeField] private LocalizedString prod;

    [SerializeField] private string ProductName;
    [SerializeField] private string ProductCategory;
    [SerializeField] private float ProductPrice;
    [SerializeField] private Sprite Image;
    [SerializeField] private int amount;
}

Trying to find ProductNameString.

{B0D8A67A-D977-41DB-8BB0-6F9CC5C8A654}

1 Like