Search Unity

How to connect two binding fields relative?

Discussion in 'UI Toolkit' started by watsonsong, Sep 24, 2021.

  1. watsonsong

    watsonsong

    Joined:
    May 13, 2015
    Posts:
    555
    I has a MonoBehavior and I want to custom the inspector with UIElements:
    Code (CSharp):
    1.  
    2. public class MyData : MonoBehaviour
    3. {
    4.     public bool featureEnabled;
    5.     public int featureParam;
    6. }
    7.  
    I hope the 'featureParam' is visible only when 'featureEnabled' is true. So I write the Editor like this:
    Code (CSharp):
    1.  
    2. [CustomEditor(typeof(MyData))]
    3. internal sealed class MyDataEditor : Editor
    4. {
    5.     private Toggle featureEnabled;
    6.     private VisualElement featureParam;
    7.  
    8.     public override VisualElement CreateInspectorGUI()
    9.     {
    10.         var asset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
    11.             "Assets/MyDataEditor.uxml");
    12.         var root = asset.CloneTree();
    13.  
    14.         this.featureEnabled = root.Q<Toggle>("featureEnabled");
    15.         this.featureParam = root.Q("featureParam");
    16.  
    17.         this.featureEnabled.RegisterValueChangedCallback(this.OnFeatureChanged);
    18.         this.featureParam.SetEnabled(this.featureEnabled.value);
    19.  
    20.         return root;
    21.     }
    22.  
    23.     private void OnFeatureChanged(ChangeEvent<bool> e)
    24.     {
    25.         // this.featureParam.visible = e.newValue;
    26.         this.featureParam.SetEnabled(e.newValue);
    27.     }
    28. }
    29.  
    There has two problem:
    - When this.featureParam.visible set to false, the UI control is disappear but the space is still there.
    - There is no way to initialize the state about 'featureParam': The root is not bind before the 'CreateInspectorGUI' return.

    Is there any best partice about this?
     
  2. griendeau_unity

    griendeau_unity

    Unity Technologies

    Joined:
    Aug 25, 2020
    Posts:
    248
    If you don't want the space to stay, you could keep a reference to your root visual element and instead do a
    featureEnabled.RemoveFromHierarchy()
    when you don't need it, then
    root.Add(featureEnabled)
    (or Insert) when you want it back.

    You can get the property from your serializedObject directly like so in the CreateInspectorGUI:

    Code (CSharp):
    1. var property = serializedObject.FindProperty("featureEnabled");
    2. featureParam.SetEnabled(property.boolValue);
     
  3. watsonsong

    watsonsong

    Joined:
    May 13, 2015
    Posts:
    555
    Yes, but 'listen to the change event and initialize the states' is a common pattern. The code somehow duplicates twice.
    The 'onChange' handler use data from 'ChangeEvent<bool>', but the 'initialize' read from serializedObject.

    It works in this way but I want to know if it is a proper way.
     
  4. watsonsong

    watsonsong

    Joined:
    May 13, 2015
    Posts:
    555
    Hi, I have two question:
    - When I 'featureEnabled.RemoveFromHierarchy()', I have to add it back to the correct position? Or is there any way to achieve this, avoid record the slibing, as is a common task.
    - Is the sequence order of 'OnEnable' and 'CreateInspectorGUI' is guaranteed? So I can find the SerializedPropertys in 'OnEnable' and store in member field after used in 'CreateInspectorGUI'
     
    Last edited: Sep 27, 2021
  5. griendeau_unity

    griendeau_unity

    Unity Technologies

    Joined:
    Aug 25, 2020
    Posts:
    248
    Binding operations will only happen once the element has been added to a panel, which has not yet happened in CreateInspectorGUI().
    You could register to `AttachToPanelEvent` and do the initialization there.

    Instead of adding to the root, you can add a simple (named) VisualElement parent to your "featureEnabled" that you would query, to easily know where it needs to be added without knowing about the hierarchy layout.
     
  6. watsonsong

    watsonsong

    Joined:
    May 13, 2015
    Posts:
    555
    Is there a good idea I change the 'style.display' between 'DisplayStyle.Flex' and 'DisplayStyle.None', instead of changing the visible?
     
  7. griendeau_unity

    griendeau_unity

    Unity Technologies

    Joined:
    Aug 25, 2020
    Posts:
    248
    Yes absolutely, sorry about that, I should have suggested that actually, much simpler solution for that part of your problem :)