Those Q(“magic!”) have been a problem from the start. Same as with getting the serialized property in editor.
I do understand the decoupling, but some don’t like runtime errors. So it would be nice to get rid of the magic strings.
One solution might be to just auto-generate a class with properly typed variables from within the UI Builder which one can then in turn reference. Or give an option to inject the variables into a C# file. Or whatever an architect who cares can come up.
But please stop it with the magic strings. Preferrably everywhere in Unity including the Editor classes. It is possible.
It’s definitely possible to use next to none, if not zero, strings with UI Toolkit, at least at runtime.
I pretty much always make custom visual elements, sometimes with the [UxmlElement] if I need to use them in the builder. Then I only need to Q<T>() for it, though the amount of queries I do are generally very few as well.
You can build your UI’s to have a few of these visual elements near the root of the UI that you query, and these can act as your primary API interface to it’s functionality. Works well with UI where most of the visual elements are code generated, which is my general approach to UI Toolkit. Understandably this works better for coders than it does designers.
I created a workaround that allows you to assign a ButtonId enum to a custom UI element that derives from button.
Then you can at least tie everything to an enum instead of a magic string. Hope this helps.
using System;
using System.Collections.Generic;
using UnityEngine.UIElements;
[Serializable]
public enum ButtonId
{
START = 1,
DEMO = 2,
TEST = 3,
}
public class CustomButton : Button
{
public ButtonId ButtonId;
public new class UxmlTraits : TextElement.UxmlTraits
{
UxmlEnumAttributeDescription<ButtonId> _buttonId = new UxmlEnumAttributeDescription<ButtonId> { name = "ButtonId" };
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
{
get { yield break; }
}
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
((CustomButton)ve).ButtonId = _buttonId.GetValueFromBag(bag, cc);
}
}
[UnityEngine.Scripting.Preserve]
public new class UxmlFactory : UxmlFactory<CustomButton, UxmlTraits> { }
public CustomButton()
{
}
}
I got it working completely without the need to create an additional type for every new button. You create duplicate buttons all of the same type and simply assign different Enums in the inspector instead of magic strings.
On the “view” side, you simply query for type and filter by ButtonId enum to assign. No magic strings anywhere to be found!