Solution
You can use a VisualTreeAsset. It's a serialized Visual Element Tree. Instantiate it and child the resulting root node to the UIdocument of your scene. Then you can query for elements and change values procedurally.
class TestMono : MonoBehaviour{
//TODO: assign these members in the inspector
[SerializeField] VisualTreeAsset myTreeAssetMember;
[SerializeField] UIDocument UIDocument;
private void Start()
{
TemplateContainer myUI = myTreeAssetMember.Instantiate();
//an example of querying for the root element of a button template with the id "submit"
Button button = myUI.Q<Button>("submit");
if (button != null)
button.clickable = new Clickable(() => Debug.Log("Stub submit button clicked."));
else
Debug.LogWarning($"Could not find \"submit\" button.");
//child the template to the UIDocument so it will be rendered and updated
UIDocument.rootVisualElement.Add(myUI);
}
}
On Asset Loading Concerns
I want to do that, but the code to load such a VisualTreeAsset that i found previously only work in the editor mode :(
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/menu.uxml"); EditorGUIUtility.Load("Assets/menu.uxml") as VisualTreeAsset;
if you know another way that doesn't require the editor, I'm all ears
Because a VisualTreeAsset is serializable you can use what's called a "direct reference" in Unity:
- Prefix a VisualTreeAsset member with
[SerializeField]
- Assign the field in the inspector.
Now Unity will automatically include it, and any other directly referenced assets, in the build. You won't have to use the AssetDatabase, or any other APIs only available when the UNITY_EDITOR is present. If for whatever reason you can't use a direct reference but still need a solution which works on device you can look into Unity's Resource API for something simple or Addressables for a more robust and scalable solution.