convert ViualTreeAsset to VisualElement

I have made a custom class( just call it 'myElement ')inherit from VisualElement, and creat or override many functions, but I don’t want build Element in C#, so I turned it to a VisualTreeAsset by define a new UxmlFactory class.
Now, I can use UIBuilder to build my ‘myElement’, I add few Labels to it’s hirechry and adjust their styles,then save it as a new Uxml asset.
Finally, I want to create this new uxml asset in C# and convert back to ‘myElemet’ class, because I need functions that I have made. I feel it is a natural way to build a customized UI element (use C# to build logical and use UIBuilder to build UI)
But I don’t figure out how to convert it.(always convert to something called TemplateContainer)

Hello! TemplateContainer is an instance of a VisualTreeAsset (or your uxml file). To get the custom class from your TemplateContainer, it really depends on how your custom class and your VisualTreeAsset are set up. Could you give us more details about how is the VisualTreeAsset being instantiated?

Let me tell you the details:

  1. here is C# code part of my custom class:
public class BaseNode : VisualElement, IPosition, ISelectable
{
    public new class UxmlFactory : UxmlFactory<BaseNode, UxmlTraits>{}
    // ui part
    public VisualElement titleContainer;
    public VisualElement contentContainer;
    public VisualElement extensionContainer;
    // function part
    public BaseNode(){}
    public void Initialize(){}  // call this function to find those Container by Q<VisualElement>(name)
    public Vector2 GetCenterPosition(){}
    public void OnAddToGraph(){}
    // .. other member functions
}
  1. here is Uxml part of my new uxml asset based on custom class:
    9565039--1352905--upload_2024-1-6_10-35-32.png
    as you can see, I build it in UIBuilder, and create a new uxml asset by right click and choose ‘Create Template’.
  2. Finally, when I want to create this new uxml asset, I need to turn it back to BaseNode class:
private VisualTreeAsset nodeAsset => EditorGUIUtility.Load(nodeAssetPath) as VisualTreeAsset;
// I want to create a new Node VisualElement
VisualElement newNode_visualElement = nodeAsset.Instantiate();
BaseNode node = (BaseNode)newNode_visualElement;

Now, I instantiate this new uxml asset, and I need to convert it back to BaseNode class. I wonder if it can works
In short, I use c# to build a custom VisualElement class and UIBuilder to build the UI hierarchy, and ultimately, I need to be able to convert my custom class from the instantiated uxml.
I have saw a lot of projects which done it all in C#(add custom class, build ui hierarchy add style class in c# etc), but this would be too cumbersome could not benefit from UI Builder.
what’s the practical way to implement custom VisualElement both need to customize c# functions and UI hierarchy.

I replied above

Your BaseNode visual element will just be the child of the TemplateContainer visual element (the one you get when you instantiate the visual tree asset).

You should just be able to Q<T> it for the node visual element.

1 Like

Really? I gonna have a try, thanks. But I also have a question that if it is a good practical way to implement customize VisualElement.

  1. inherit from visual elements and build custom logical
  2. define custom class as a uxml asset and build it in UI builder to generate new uxml asset
  3. initiate new uxml asset and Q<> to get custom VisualElement class back.
    boom done!

Iunno, does it work or doesn’t it?

From experience you still need to add the instantiated TemplateContainer to the visual tree. So in terms of manipulating visual elements, you still have to deal with the template container, which can be cumbersome.

I mostly do this kind of dynamic stuff via code, but still style the UI with USS styles.

sure I need to add it to visual tree, but I reference it only by my custom visual element, all manipulator are on my custom visual elements, why should I deal with TemplateContainer?

Well if want to remove it, you need to remove the TemplateContainer. If you only remove it via the node element, you’ll have vestigial visual elements everywhere.

ok, it is a problem.But it can be easily resolved by add some logicals. Any other severe problems? I think build UI in c# is too annoying, and no good for fast iteration.

Iunno, why don’t you start implementing it and tell us?

hahahaha I’m outside right now, I will try it as soon as possible and let you know my progress.

I just recently started working with UITK thanks to a work project, and I’ve been using UI Builder to create the “prefab” UIs and then adding them to a main UI document at runtime. If you need a link to the base element you can do it like this. In my case I name them “root” but just substitute with your name.

var root = prefab.Instantiate().Q<VisualElement>("root");

The “root” is your custom class inherit from Visual Elements and “prefab” is Template created from UIBuilder, right?

No. It’s just a blank visual element but I don’t see any reason why it couldn’t be a custom class. Switching to a custom class is as simple as changing the type in the query.

var root = prefab.Instantiate().Q<MyCustomClass>("root");

Yes.

Scanning through the APIs for creating custom classes I don’t see any reason why you couldn’t create a custom class that can be placed in the UI Builder whose sole purpose is to automatically instantiate a VisualAssetTree.

https://docs.unity3d.com/ScriptReference/UIElements.UxmlTypeAttributeDescription_1.html

 public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
    {
        switch (SearchTreeEntry.userData)
        {
            case "createFromUxml":
            {
                // BaseNode node1 = new BaseNode();
                if (nodeAsset != null)
                {
                    BaseNode node = nodeAsset.Instantiate().Q<BaseNode>();
                    if (node != null)
                    {
                        node.GetUxmlVisualElement(); // retrieve visualElement properties
                        graphView.AddElement(node);
                    }
                }
                return true;
            }
            case "createFromC#":
            {
                BaseNode node = new BaseNode();
                node.DeprecatedConstructed(); // create visualElements in c# 
                graphView.AddElement(node);
                return true;
            }
            default:
            {
                return false;
            }
        }
    }

9567922--1353793--upload_2024-1-8_11-8-54.png
Here is a simple test, they are two nodes created from c# and uxml, I think it works no difference