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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Bug My Custom VisualElement does not bind correctly with my property

Discussion in 'UI Toolkit' started by Magnilum, Sep 9, 2023.

  1. Magnilum

    Magnilum

    Joined:
    Jul 1, 2019
    Posts:
    181
    Hello,

    I have created a class named Template with an Vector2Int variable.

    Code (CSharp):
    1. public class Template : ScriptableObject {
    2.  
    3.     [SerializeField] Vector2Int _densityRange = new Vector2Int(1, 5);
    4. }
    I am trying to create a custom inspector for this class:

    Code (CSharp):
    1. [CustomEditor(typeof(Template))]
    2. public class TemplateEditor : Editor {
    3.  
    4.     [SerializeField] VisualTreeAsset document;
    5.  
    6.     VisualElement root;
    7.  
    8.     MinMaxIntSlider slider;
    9.     Vector2IntFiel vect2;
    10.  
    11.     public override VisualElement CreateInspectorGUI()
    12.     {
    13.         root = document.CloneTree();
    14.  
    15.         slider = root.Q<MinMaxIntSlider>("slider");
    16.         vect2 = root.Q<Vector2IntField>("vector2");
    17.  
    18.         slider.bindingPath = "_densityRange";
    19.         vect2.bindingPath = "_densityRange";
    20.  
    21.         return root;
    22.     }
    23. }
    When everthing is compiled, the Vector2 take automatically the corresponding values of the scriptableObject variable but not the slider. But If I move the slider, the vector2 follow the values of the slidet which is great. Then when I try to change the values in the vector2, the slider do not moves.

    Here is the code of my MinMaxIntSlider:

    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
    2.     <Style src="project://database/Assets/PrefabPainter/UI%20Toolkit/Style/MinMaxSlider.uss?fileID=7433441132597879392&amp;guid=8aebbbb8728f1c44f81526014c86d0d8&amp;type=3#MinMaxSlider" />
    3.     <ui:VisualElement style="flex-direction: row; align-items: center; height: 25px; background-color: rgb(46, 46, 46); border-top-left-radius: 10px; border-bottom-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; overflow: hidden;">
    4.         <ui:Label tabindex="-1" text="Min Max Slider" parse-escape-sequences="true" display-tooltip-when-elided="true" name="label" style="padding-left: 10px; padding-right: 0; padding-top: 0; padding-bottom: 0; min-width: 120px; height: 100%; -unity-text-align: middle-left; background-color: rgb(36, 36, 36);" />
    5.         <ui:IntegerField value="0" name="minValue" />
    6.         <ui:MinMaxSlider picking-mode="Ignore" min-value="0" max-value="40" low-limit="0" high-limit="100" name="slider" style="margin-left: 0; margin-right: 0; margin-top: 0; margin-bottom: 0; flex-grow: 1; align-self: stretch; align-items: center; padding-left: 2px; padding-right: 2px;" />
    7.         <ui:IntegerField value="5" name="maxValue" />
    8.     </ui:VisualElement>
    9. </ui:UXML>
    10.  
    Code (CSharp):
    1. public class MinMaxIntSlider : BaseField<Vector2Int> {
    2.  
    3.     public new class UxmlFactory : UxmlFactory<MinMaxIntSlider, UxmlTraits> { }
    4.     public new class UxmlTraits : BaseField<Vector2Int>.UxmlTraits
    5.     {
    6.         private UxmlIntAttributeDescription m_MinValue = new UxmlIntAttributeDescription { name = "min-value", defaultValue = 0 };
    7.         private UxmlIntAttributeDescription m_MaxValue = new UxmlIntAttributeDescription { name = "max-value", defaultValue = 60 };
    8.         private UxmlIntAttributeDescription m_LowLimit = new UxmlIntAttributeDescription { name = "low-limit", defaultValue = 0 };
    9.         private UxmlIntAttributeDescription m_MaxLimit = new UxmlIntAttributeDescription { name = "hight-limit", defaultValue = 100 };
    10.  
    11.         public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    12.         {
    13.             base.Init(ve, bag, cc);
    14.  
    15.             MinMaxIntSlider minMaxSlider = ve as MinMaxIntSlider;
    16.  
    17.             minMaxSlider.minValue = m_MinValue.GetValueFromBag(bag, cc);
    18.             minMaxSlider.maxValue = m_MaxValue.GetValueFromBag(bag, cc);
    19.             minMaxSlider.lowLimit = m_LowLimit.GetValueFromBag(bag, cc);
    20.             minMaxSlider.hightLimit = m_MaxLimit.GetValueFromBag(bag, cc);
    21.         }
    22.     }
    23.  
    24.     UnityEngine.UIElements.MinMaxSlider slider;
    25.     IntegerField minValueField;
    26.     IntegerField maxValueField;
    27.  
    28.     public float minValue
    29.     {
    30.         get => slider.minValue;
    31.  
    32.         set {
    33.             if (value < lowLimit)
    34.                 slider.minValue = lowLimit;
    35.             else if (value > maxValue)
    36.                 slider.minValue = maxValue;
    37.             else
    38.                 slider.minValue = value;
    39.  
    40.             minValueField.value = (int)slider.minValue;
    41.         }
    42.     }
    43.  
    44.     public float maxValue
    45.     {
    46.         get => slider.maxValue;
    47.      
    48.         set {
    49.             if (value < minValue)
    50.                 slider.maxValue = minValue;
    51.             else if (value > hightLimit)
    52.                 slider.maxValue = hightLimit;
    53.             else
    54.                 slider.maxValue = value;
    55.  
    56.             maxValueField.value = (int)slider.maxValue;
    57.         }
    58.     }
    59.  
    60.     public float lowLimit
    61.     {
    62.         get => slider.lowLimit;
    63.      
    64.         set {
    65.             slider.lowLimit = value < hightLimit ? value : hightLimit;
    66.  
    67.             if (minValue < slider.lowLimit)
    68.                 minValue = slider.lowLimit;
    69.  
    70.             if (maxValue < slider.lowLimit)
    71.                 maxValue = slider.lowLimit;
    72.         }
    73.     }
    74.  
    75.     public float hightLimit
    76.     {
    77.         get => slider.highLimit;
    78.      
    79.         set {
    80.             slider.highLimit = value > lowLimit ? value : lowLimit;
    81.  
    82.             if (maxValue > slider.highLimit)
    83.                 maxValue = slider.highLimit;
    84.  
    85.             if (minValue > slider.highLimit)
    86.                 minValue = slider.highLimit;
    87.         }
    88.     }
    89.  
    90.     public MinMaxIntSlider() : this("Min Max Slider", 0, 40, 0, 100)
    91.     {
    92.      
    93.     }
    94.  
    95.     public MinMaxIntSlider(string label, float minValue, float maxValue, float lowLimit, float hightLimit) : base(label, null)
    96.     {
    97.         RemoveFromClassList("unity-base-field");
    98.  
    99.         VisualTreeAsset document = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/PrefabPainter/UI Toolkit/MinMaxIntSlider.uxml");
    100.         document.CloneTree(this);
    101.  
    102.         slider = this.Q<UnityEngine.UIElements.MinMaxSlider>("slider");
    103.         minValueField = this.Q<IntegerField>("minValue");
    104.         maxValueField = this.Q<IntegerField>("maxValue");
    105.  
    106.         slider.RegisterValueChangedCallback((evt) =>
    107.         {
    108.             int x = (int)evt.newValue.x;
    109.             int y = (int)evt.newValue.y;
    110.             minValueField.value = x;
    111.             maxValueField.value = y;
    112.             value = new Vector2Int(x, y);
    113.         });
    114.  
    115.         minValueField.RegisterValueChangedCallback((evt) =>
    116.         {
    117.             this.minValue = evt.newValue;
    118.         });
    119.  
    120.         maxValueField.RegisterValueChangedCallback((evt) =>
    121.         {
    122.             this.maxValue = evt.newValue;
    123.         });
    124.  
    125.         this.label = label;
    126.         this.minValue = minValue;
    127.         this.maxValue = maxValue;
    128.         this.lowLimit = lowLimit;
    129.         this.hightLimit = hightLimit;
    130.     }
    131. }
    I know this is a very long code and not clean so if you have any suggestion to clean it up a bit, it would be great.

    Coming back to the main point, I don't understand why it does not bind with the property since it inherit from the BaseField<> class like the Vector2IntField. For sure I missed something but I really don't know what.
     
    Last edited: Sep 9, 2023