Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Feature Request Is there any way to access attributes bag after visual element was instantiated?

Discussion in 'UI Toolkit' started by Hellfim, Jun 1, 2023.

  1. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
    Suppose I have defined following control:

    Code (csharp):
    1.  
    2. public partial class MyVisualElement : VisualElement, IBindableElement
    3. {
    4.     public new class UxmlFactory : UxmlFactory<MyVisualElement, UxmlTraits> { }
    5.     public new class UxmlTraits : VisualElement.UxmlTraits
    6.     {
    7.         public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    8.         {
    9.             base.Init(ve, bag, cc);
    10.                
    11.             bag.TryGetAttributeValue("custom-attribute", out var value);
    12.         }
    13.     }
    14. }
    15.  

    I can invoke TryGetAttributeValue to access any attribute defined in uxml file inside Init method of UxmlTraits.

    I would like to be able to access attributes of VisualElement which are not defined in C# with UxmlAttribute/UxmlTraits outside of custom UxmlTraits class. Something like myVisualElement.bag.TryGetAttributeValue(...).
    I need this to be able to extend default behaviour of VisualElements just by adding an attribute.

    Is there any chance for this feature to be implemented? Am I missing something and there is a way already?
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,842
    You're missing something, yes. Refer to these docs: https://docs.unity3d.com/Manual/UIE-expose-custom-control-to-uxml.html

    You get values from the Uxml attributes, and you can assign those values to fields/properties in the class itself, or types it inherits from. Such as so:

    Code (CSharp):
    1. public partial class MyVisualElement : VisualElement, IBindableElement
    2. {
    3.     private int _someInt;
    4.    
    5.     public int SomeInt => _someInt;
    6.    
    7.     public new class UxmlFactory : UxmlFactory<MyVisualElement, UxmlTraits> { }
    8.    
    9.     public new class UxmlTraits : VisualElement.UxmlTraits  
    10.     {
    11.         UxmlIntAttributeDescription m_someInt = new UxmlIntAttributeDescription { name = "some-int" };
    12.        
    13.         public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    14.         {
    15.             base.Init(ve, bag, cc);
    16.             var mve = (MyVisualElement)ve;
    17.             mve._someInt = m_someInt.GetValueFromBag(bag, cc);
    18.         }
    19.     }
    20. }
     
  3. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
    spiney199, thanks for answering, but that's not what I've asked about. I do know that it's possible to make a cusom control in C# with attributes.

    I want to be able to add custom attributes through in uxml, i.e.:
    <VisualElement my-custom-attribute="1234" />
    and be able to read it from any other class which has access to that element

    Note that I want to modify Unity's VisualElement and not my customly-declared MyVisualElement
     
  4. griendeau_unity

    griendeau_unity

    Unity Technologies

    Joined:
    Aug 25, 2020
    Posts:
    230
    There is no way to add attributes to existing internal Unity classes. These are precompiled and cannot be modified from users' projects. To add attributes, you need to create a custom type with its own UxmlTraits, as @spiney199 suggested.
     
  5. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
    griendeau_unity, I believe that I wasn't clear in my suggestion if you are giving me this answer. I'll try to clarify myself.

    Suppose I have a following Text.uxml file and a MyVisualElement from first post
    Code (csharp):
    1.  
    2. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    3.     <ui:VisualElement name="unityVisualElement" custom-attribute="my-custom-attribute-value"/>
    4.     <MyVisualElement name="myVisualElement" custom-attribute="my-custom-attribute-value"/>
    5. </ui:UXML>
    6.  

    I'm able to access 'custom-attribute' inside MyVisualElement's UxmlTrait.Init. Please, pay attention, that MyVisualElement does NOT have a corresponding declared attribute in C# like
    Code (csharp):
    1.  
    2. [UxmlAttribute("custom-attribute")]
    3. private String _customAttribute;
    4.  
    and yet, attribute 'custom-attribute' and it's value 'my-custom-attribute-value' arbitrarily defined in UXML are accessable from that Init method.

    This proves that the bag (IUxmlAttributes) [or something what fills it] correctly parses UXML and collects all attributes with values defined there regardless of those attributes (not)being declared in C# control.

    So my suggestion is to expose this bag as a property of a VisualElement, so it will be possible to do something like
    Code (csharp):
    1.  
    2. unityVisualElement.bag.TryGetAttributeValue("custom-attribute", out var value);
    3.  
    wherever I have access to a unityVisualElement.
     
  6. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
  7. DarkRewar

    DarkRewar

    Joined:
    Jan 19, 2015
    Posts:
    18
    I think it is impossible to have access to bag at runtime, simply because bag doesn't exist anymore when your view is rendered.

    What is happening, the Unity UI Builder reads from the UXML file your XML architecture and, then, import it into a VisualTreeAsset which is ready for runtime (without bag).

    The only way to get the "bag" is to deserialize the XML yourself by referencing the UXML file and go through each markup to find yours and get attributes you want.
     
  8. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
    DarkRewar, bag is certainly accessable during runtime as I displayed in the code in the first post (inside Init method).
    So it certainly possible to implement a new bag property which will hold that value.
     
  9. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
    Still believe that it's a good suggestion, so bumping thread to have some input from Unity team
     
  10. griendeau_unity

    griendeau_unity

    Unity Technologies

    Joined:
    Aug 25, 2020
    Posts:
    230
    UxmlFactories and UxmlTraits were added to provide an alternative to Unity's serialization which didn't have all the necessary features to support UI Toolkit's needs for UXML at the time. So the idea is that we always have a backing property to hold the serialized value after import. We planned to come back to Unity's serialization at some point and that's part of the reason why we don't expose the bag, as we don't want to enable practices that won't be applicable in a subsequent release.

    These changes recently landed in our latest Alpha, so attribute bags should soon be deprecated in favor of serialization. See this documentation on the new system for more details:
    https://docs.unity3d.com/2023.2/Documentation/Manual/UIE-custom-controls.html
     
    DarkRewar and Hellfim like this.
  11. Hellfim

    Hellfim

    Joined:
    Sep 11, 2014
    Posts:
    92
    Got it thanks for the detailed answer!
     
    griendeau_unity likes this.