Is it possible to extend VisualTreeAsset?

Hi, I’m wondering, since VisualTreeAsset is a ScriptableObject is there a way to create custom VisualTreeAsset? e.g.

[Serializable]
public class SomeUIPanel : VisualTreeAsset
{
    // bindings and event handlers here
}

Right now the only straightforward way to associate C# code with UXML element is by extending VisualElement, however you either have to generate UXML manually or load VisualTreeAsset which is not ideal.

I mean it’s not sealed, so inherit away. I’m not sure what it would accomplish though. None of its code has been written with inheritance in mind.

Maybe in older versions but in Unity 6 onwards we can easily make custom visual elements with a few simple attributes: Unity - Scripting API: UxmlElementAttribute

Then styling the element can easily be done with style sheets, or with a Theme Style Sheet if needing to do so globally.

Yeah, I did inherit from it, but I think (I may be wrong) that in order to turn it into UXML asset instance I need to call internal code :frowning: .
I’m aware of the Unity 6 custom VisualElement API, but I’m looking for something which will allow me to encapsulate both C# and UXML in a single asset, this is why I’m interested in VisualTreeAsset in particular.

I don’t think there’s anyway to do that right now. UXML is deserialised and used to generate the runtime C# classes. The UXML attributes are simply a way for source generators to make all the boilerplate for us. But at the end of the day, the relationship still is still one way. UXML → C# Visual elements, which then goes on to get used for rendering and other things.

I’m not sure what advantage you’d get by wanting to couple these two together so tightly.

The main reason for coupling UXML and C# using VisualTreeAsset is that they do belong together in terms of an asset, plus this way it can be possible to use the asset as a data source and since it is a ScriptableObject – serialized fields can be used to provide dependencies.

I feel like that is mixing together too much responsibility. Your visual element classes obviously handle UI stuff. The visual tree asset handles the serialisation and instantiation.

You can still inject dependencies with [UxmlAttribute] fields in your visual elements.

I get where you coming from, however I’m trying to avoid following OOP principles unless they solve my problems. As in this case – I want to have Balance asset which displays player’s currency, currently there are a two ways to do it:

  1. Inherit from VisualElement
  • generate UXML in the constructor or load and instantiate a VisualTreeAsset,
  • bind player’s balance using C# or use runtime binding system

I do not want generate UXML via C# or load it (my use cases do not require a lot of UXML)

  1. Create VisualTreeAsset (Create>UI Toolkit>UI Document)
  • author UXML in UI Builder or using text editor
  • bind player’s balance using runtime binding system

This method works for a simple thing like player’s balance, but there is no way to co-locate any C# logic like event handlers, etc.

Ideally, I would like to just extend VisualTreeAsset in the following way:

public class PlayerBalance : VisualTreeAsset {
    [SerializeField] private UIDialog currencyShop;

    [Inject] private IUIService _uiService;
    [Inject] private ISaveService _saveService;

    // bind UXML to this property using UI builder
    public int Balance => _saveService.playerBalance;

    // I use UnityEvent inside UI builder, so I can bind this handler as well
    public void OpenShop() {
        _uiService.OpenDialog(currencyShop);        
    }
}

Since, VisualTreeAsset is a ScriptableObject, I would just drop it to the default data source field in UI Builder to easily bind both Balance and OpenShop() in UI Builder.
Additionally would make it easier to transfer between projects, since it’s a single asset containing both UXML and C# logic (I do handle styles to avoid VisualTreeSpecific stylesheets).

Why not just write your own scriptable object type for this? Inheriting from VisualTreeAsset doesn’t add anything here.

1 Like

Inheriting from VisualTreeAsset allows me to just grab PlayerBalance.uxml asset generated from PlayerBalance.cs (example above) and just drop it in UI Builder (in the UXML tree specifically, not in the attributes drawer) on any other asset I need, e.g. Menu.uxml, Shop.uxml.

Essentially, what I’m looking for is a UXML template which has co-located C# logic along with the benefits of a ScriptableObject.

I mean you’re not really solving any problems here you’re just lumping two problems together into the same asset. Two problems that would be better off handled by two different assets.

VisualTreeAsset should just have the one responsibility of serializing our UXML and instantiating VisualElement’s from it. There’s no real benefit to be had by jamming other responsibility in other than a few less assets in the project, which isn’t much of a benefit imo.

Well I’m certainly not trying to adhere to SRP, there is a specific workflow I would like to implement which is – co-locate logic and structure of a self-contained UI element. Thanks for your feedback, anyway!