Search Unity

UIElements and ScriptableObjects in EditorWindow

Discussion in 'UI Toolkit' started by Sangemdoko, Aug 16, 2019.

  1. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    220
    Hi,

    I am trying to draw the fields of a scriptableObject when I drop it in an editor window. Currently I can draw the fields using PropertyField visualElement but editing the fields does not edit the actual scriptableObject. It is as if the binding is broken/undone after the elements are drawn.

    I could draw the fields manually and change the values by registering ChangeValue callbacks but that's not really the point. I just want to understand what I'm doing wrong before going forward.

    So here are the relevant pieces of editor code

    Code (CSharp):
    1.  
    2. void Intialize()
    3. {
    4.     m_MyScriptableObjectField = new ObjectField("MyScriptableObject Object");
    5.     m_MyScriptableObjectField.objectType = typeof(MyScriptableObject);
    6.     m_MyScriptableObjectField.RegisterValueChangedCallback(c => {
    7.         m_MyScriptableObjectDisplayContainer.Clear();
    8.         m_MyScriptableObjectDisplayContainer.Add(MyScriptableObjectDisplay((MyScriptableObject)c.newValue));
    9.     });
    10.  
    11.     Add m_MyScriptableObjectField);
    12.     Add(m_MyScriptableObjectDisplayContainer);
    13. }
    14.  
    15.  
    16. VisualElement MyScriptableObjectDisplay(MyScriptableObject MyScriptableObject)
    17. {
    18.     var container = new VisualElement();
    19.  
    20.     container.Add(new Label("MyScriptableObject Object settings"));
    21.     if (MyScriptableObject == null) {
    22.         container.Add(new Label("please set a MyScriptableObject object"));
    23.     } else {
    24.         container.Add(CreateUIElementInspector(MyScriptableObject, null));
    25.     }
    26.  
    27.     return container;
    28. }
    29.  
    30. public static VisualElement CreateUIElementInspector(UnityEngine.Object obj, List<string> propertiesToExclude)
    31. {
    32.     var container = new VisualElement();
    33.  
    34.     var serializedObject = new SerializedObject(obj);
    35.  
    36.     var fields = GetVisibleSerializedFields(obj.GetType());
    37.  
    38.     for (int i = 0; i < fields.Length; ++i) {
    39.         var field = fields[i];
    40.         // Do not draw HideInInspector fields or excluded properties.
    41.         if (propertiesToExclude != null && propertiesToExclude.Contains(field.Name)) {
    42.             continue;
    43.         }
    44.  
    45.         //Debug.Log(field.Name);
    46.         var serializedProperty = serializedObject.FindProperty(field.Name);
    47.  
    48.         var propertyField = new PropertyField();//needs to be set on two lines
    49.         propertyField.BindProperty(serializedProperty);
    50.  
    51.         container.Add(propertyField);
    52.     }
    53.  
    54.  
    55.     return container;
    56. }
    57.  
    58. public static FieldInfo[] GetVisibleSerializedFields(Type T)
    59. {
    60.     List<FieldInfo> infoFields = new List<FieldInfo>();
    61.  
    62.     var publicFields = T.GetFields(BindingFlags.Instance | BindingFlags.Public);
    63.     for (int i = 0; i < publicFields.Length; i++) {
    64.         if (publicFields[i].GetCustomAttribute<HideInInspector>() == null) {
    65.             infoFields.Add(publicFields[i]);
    66.         }
    67.     }
    68.  
    69.     var privateFields = T.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
    70.     for (int i = 0; i < privateFields.Length; i++) {
    71.         if (privateFields[i].GetCustomAttribute<SerializeField>() != null) {
    72.             infoFields.Add(privateFields[i]);
    73.         }
    74.     }
    75.  
    76.     return infoFields.ToArray();
    77. }
    The idea is that when I change the content of the ObjectField the properties are (re)drawn using propertyFields.
    The fields are grabbed automatically using some Reflection

    Thank you for your time
     
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Avoid using
    propertyField.BindProperty(serializedProperty);
    . It's for a very specific use case. Just remove that line and simply call
    container.Bind(serializedProperty);
    after your for loop. Also, change your construction of
    new PropertyField();
    (back) to
    new PropertyField(serializedProperty);
    .
     
  3. Sangemdoko

    Sangemdoko

    Joined:
    Dec 15, 2013
    Posts:
    220
    I missed that message. Thank very much it works great now! I think I understand why it didn't work before. Here is the code for other people if they are interested.

    Code (CSharp):
    1. public static VisualElement CreateUIElementInspector(UnityEngine.Object target, List<string> propertiesToExclude)
    2.         {
    3.             var container = new VisualElement();
    4.  
    5.             var serializedObject = new SerializedObject(target);
    6.  
    7.             var fields = GetVisibleSerializedFields(target.GetType());
    8.  
    9.             for (int i = 0; i < fields.Length; ++i) {
    10.                 var field = fields[i];
    11.                 // Do not draw HideInInspector fields or excluded properties.
    12.                 if ( propertiesToExclude != null && propertiesToExclude.Contains(field.Name)) {
    13.                     continue;
    14.                 }
    15.                
    16.                 var serializedProperty = serializedObject.FindProperty(field.Name);
    17.  
    18.                 var propertyField = new PropertyField(serializedProperty);
    19.  
    20.                 container.Add(propertyField);
    21.             }
    22.            
    23.             container.Bind(serializedObject);
    24.  
    25.  
    26.             return container;
    27.         }