How to make a clone/copy of a VisualElement?

How do I clone a VisualElement?

Like, newElement = oldElement.Clone(); or newElement = new VisualElement(oldElement);

3 Likes

Hi,

Thereā€™s no way to clone a VisualElement.
What you can do instead is to create a new VisualElement and then set the same USS classes, event handlers and so on.

Thank you for the reply.

The thing is that the visual element has several nested children as well. Manually copying everything is pretty tedious. I guess a method that manually copies everything could be created, but I feel a simple copy/clone method should be part of the standard API. I can think of several use cases for it and feel that will be a fairly common request.

Is the planned? If not, why not?

3 Likes

To my knowledge this is the first time that a request for VisualElement cloning comes.
I donā€™t think this is planned, but Iā€™ll start a discussion about it.

Meanwhile if you want to clone hierarchy of elements Iā€™d recommend using UXML with VisualTreeAsset.CloneTree. This way you can create a tree of elements with the same set of properties.

A clone function may sound really simple on the surface, but it actually isnā€™t. There are many internal properties to a VisualElement and some of them are trivial to clone and others are not or must not be cloned at all. Thereā€™s also the case of element inheriting from VisualElementā€¦

1 Like

From my testing, itā€™s best to use an MVC pattern with a Clone function on the model it will automatically deliver the correct VisualElement from your view.

I appreciate your reply and your explanation, thank you. That makes a lot of sense.

I understand that visualTreeAsset.CloneTree is the only way to create/clone elements without creating them through C#. My problem is that there are a lot of different element-trees that I want to clone/copy for lists and such. I do not wish to create those manually with code as they are quite complex and I want to iterate quickly, and having to create different UXML files for every element-tree I want to clone will create a lot of UXML files which is a bit tedious. Is there a way (or is it even feasible) to use CloneTree on just a part of a tree asset? Like querying (by class or name) for a specific element within a tree asset and creating a clone of just that element?

Thank you for your time.

From what I understand this statement is contradictory, if the element-trees are not created from UXML they must have been created manually from code. If that is the case itā€™s just a matter of creating some functions that would create the desired hierarchy and reuse that code where needed.

But to answer your question CloneTree works on a full VisualTreeAsset only.

Finally, may I suggest that you try to approach your problem in a different way?
There are many other options to VisualElement cloning, I donā€™t see any compelling reasons to do so personally but I may be missing something. And if itā€™s not enough you still have the possibility of creating your own cloning functionā€¦

This seems like a shameā€¦ Can these be nested?

UI stuff really needs the ability to be swappable with other elements. I should be able to take a whole form within an EditorWindow and swap it out with a different formā€¦

Iā€™m already very hesitant on VisualElements and this isnā€™t helpingā€¦ :frowning:

This is totally possible with UIElements, you can modify the tree returned by CloneTree as much as you want.

Thatā€™s good to know ā€“ I was assuming it would change the original.

This makes me feel a bit better about it.

Might be near of topic, how about use instead override class to create layout itself?
VisualElement is not sealed class, so you can override it.

public class MyCustomElement : VisualElement
{
    public MyCustomElement() : base() {
        //TODO : Create layout
        this.AddManipulator(new MyCustomElementEvent());
    }
 
    class MyCustomElementEvent : Manipulator
    {
        //TODO : Register, Unregister event and write event's logic.
    }
 
    //Sure if you want to clone data from some source...
    public void CopyFrom(MyCustomElement source) {
        //TODO : copy value, layout data from source...
    }
}

ā€˜UIElements base class is overridableā€™ means lot of possibilities to customization, so able to make own scripted, functional element.
You should try with it.

I had with this possibilities.

4878851--470768--ME3MG8d.gif

1 Like

Although we encourage encapsulation where possible, VisualElement was made to be inherited from and overridden. This is how you make custom elements with custom behavior, like a TreeView.

2 Likes

Can you get someone to do this override magic for the SceneCameraController so that I can make my own SceneCameraController for the SceneView please?

I want my orbit behavior to work differently than the standard Unity oneā€¦

I will like to support the clone function on a visual element. I am new to UI Elements and I may be doing something wrong or thinking it in a wrong way, so feel free to correct/guide me.

Coming from the android background. I am trying to do a dynamic ListView and separating the adapter .uxml files In order to reutilize them in different list views. So each time that I am using them in a list view I need to first create a clone to know the height of the layout. And then each time that a new item is required, create a copy of the adapter file from the VisualTreeAsset.

Is that the way it is meant to work? In my head, I want to get a concrete visual element and N copies of it. Instead of copy the whole file. If I only want to populate a listview with part of the adapter I should be able to create copies of the specific part. If not, I am forced to separate each part of the adapter in each file and build them manually or lookup for the specific part each time that a new object is required.

Is that correct? I am missing something?

As a webdev I really like the way UI Toolkit is done and I see a lot of people struggle with the concept in some ways. Hope this will shed some light on the concept.
Try to imagine the UXML assets as templates/components/views. So, letā€™s say you want a list of players. So you create the base template containing the layout, the ListView, etc. Then you want to design each entry of the player list. So you go ahead and create some playerentry.uxml asset, design that the way youā€™d like the player entries to look in the list. And then, via script, you set the itemSource, makeItem and bindItem functions of the ListView. In the makeItem function, you can use the CloneTree method on the playerentry.uxml asset which will populate the list of players with your designed entries automagically. Then you can use the bindItem function to set the proper data values on the playerentry subelements.

TL;DR: I think creating multiple UXML assets (for base templates, specific child items etc.) is the proper way to go as it will separate neccessary views and will be more organized in a long run. Didnā€™t really think of a need for Clone function as of yet.

1 Like

Just to shed more light, as Refeas above mentione, UXML is the ā€œcloneā€ feature you are looking for. The C# equivalent is the standard ā€œC# functionā€ (you can always create a function that creates a specific set of element in C#, so kinda like a ā€œcloneā€). For something like ListView, indeed, youā€™d want two UXML files, one for the main UI that contains the ListView and one for the ā€œtemplateā€ of each of the items.

As for why itā€™s not possible to clone a C# VisualElement. This is because elements are C# Objects. They have non-trivial constructors which can often create more child elements, state that cannot be serialized like C# Properties, references to other VisualElements, and often registered callbacks made by interested parties. Also VisualElements have no unique identifiers so if one VisualElement references another, this reference is only stable at runtime while those two elements exist as Objects. It needs to be recreated when the elements are recreated.

While it would be nice to be able to ā€œcloneā€ a UI element, it would prevent us from having the advantages we have now that they are actual C# objects. Hence, to get something close to a clone, we implemented the UXML approach.

The UI Elements Debugger clearly shows that youā€™re able to convert the C# objects to uxml. You list all the VisualElements and all their classes, names, inline properties, etc. You can pretty trivially build uxml from that.

At the same time, youā€™re obviously able to generate the VisualElement objects from uxml.

So it should not be hard to implement clone through first generating the uxml from the VisualElement, then creating new VisualElements from that uxml.

Doing that approach would also have two huge benefits:

  • Itā€™d really be nice to output the generated uxml, both for debugging and for editor tools that wants to build UI.
  • Itā€™d be really nice at times to generate VisualElements from a uxml string rather than an asset. Thatā€™d allow things like implementing a newsfeed in your main menu simply by downloading the uxml/uss text from a url.

Does the uxml asset wrapper contain some kind of accelerator/serialized version of the generated objects rather than just the text? If it just parses the uxml file from text at runtime, the only thing keeping this from already working is access modifiers on Unity internals, but it could be more complex, idk.

This is not an accurate conversion. Itā€™s just a bit of help to have something to start with. It may not even be valid UXML. It just reflects the VisualElement types and generates UXML with the full type name. A lot of information is lost.

As I explained above, UXML ā†’ VisualElements is a one-way conversion, where the end result has more information and state than can be represented in UXML. Going back from VisualElement ā†’ UXML is not generally possible for the same reason why compiled object files cannot be converted back to source code. Yes, you ā€œcanā€ do an approximated conversion, with lots of exceptions and pitfalls, and you are free to do that for your own code in the same way the UI Debugger does it (if you get the UI Toolkit package, the Debugger code is there so you can see how it generates that UXML). But we wonā€™t release such a feature because the expectation will be that it works ā€œall the timeā€ and reliably.

This will be possible at some point. We just didnā€™t make the function you need public. Itā€™s a method on our UXML importer that we use after we read the contents of the .uxml file. UI Builder converts UXML to VisualElements in memory all the time. If you copy some elements in the Builder and you paste in a text editor, youā€™ll see that what you copied was UXML. The UI Builder can generate UXML in this case because it uses the main asset UXML you have open as the source, not the live VisualElements you see in the work area. It just extracts the bit of UXML from main asset.

Yes, the VisualTreeAsset, which is the C# object generated from importing a text-based .uxml file, is indeed optimized for runtime use. We do not parse UXML at runtime by default for performance reasons.

Hi there.

Instead of cloning, Iā€™m happy to build from an UXML, but Iā€™m not keen on having hand coded paths kicking around, or lots of hand defined references - Iā€™d rather find it within an existing scene. It seems to me that an ideal bridge would be to:

  • Define the object that I would want to clone from a UXML (letā€™s call that UXMLCreatedObject)
  • Place it within my heirarchy (another UXML) defining my screen as normal
  • In c#, find the UXMLCreatedObject.
  • Dig out itā€™s source UXML file
  • Create another one.

Is this possible?

So if I understand correctly, you would like to centralize all of your asset references in one place ? Basically only have a reference to one asset which internally serves as the library for all object types to create ?

If thatā€™s so, I would recommend looking into creating a custom Scriptable Object which accomplishes the same job:

[CreateAssetMenu(menuName = "My Assets/TVisualTreeAsset References")]
public class VisualTreeAssetReferences : ScriptableObject
{
    public VisualTreeAsset ref1;
    public VisualTreeAsset ref2;
}

Then create an instance of that and manage an ā€œhardcodedā€ path to it. If youā€™re working with game UI you can just pass a reference to it.

Then in the Inspector you can just drag and drop references to your different UXML files, use VisualTreeAsset.CloneTree() as described above.

1 Like