Search Unity

Bug EnumField is not compatible with Enum property

Discussion in 'UI Toolkit' started by MacDx, Jun 10, 2019.

  1. MacDx

    MacDx

    Joined:
    Feb 1, 2014
    Posts:
    5
    Hi. I'm doing a custom editor window and I'm trying to bind an EnumField to an enum property of my custom enum type. But I get the warning:
    Field type UnityEditor.UIElements.EnumField is not compatible with Enum property "myEnum"
    The enum field works but it isn't bound so my data object does not update and so when I hit play on the editor, the field loses the information and is set to default. In contrast, the text field works as expected since it retains the correct value.

    This is my code.

    Code (CSharp):
    1.  
    2. //DataObject.cs
    3. //Object for binding
    4. public class DataObject : ScriptableObject
    5. {
    6.     public MyEnum myEnum;
    7.     public string myString;
    8.  
    9.     void OnEnable()
    10.     {
    11.         hideFlags = HideFlags.HideAndDontSave;
    12.  
    13.         if (myString == null)
    14.             myString = "";
    15.     }
    16. }
    17.  
    18. //TestWindow.cs
    19. public enum MyEnum
    20. {
    21.    Foo1,
    22.    Foo2,
    23.    Foo3
    24. }
    25.  
    26. public class TestWindow : EditorWindow
    27. {
    28.    [SerializeField]
    29.    private DataObject dataObject;
    30.  
    31.    private TextField textField;
    32.    private EnumField enumField;
    33.    
    34.    [MenuItem("Tools/Test Window")]
    35.    private static void ShowEditor()
    36.    {
    37.       TestWindow window = GetWindow<TestWindow>();
    38.       window.titleContent = new GUIContent("Test Window");
    39.    }
    40.  
    41.    void OnEnable()
    42.    {
    43.       textField = new TextField("Text");
    44.       textField.bindingPath = "myString";
    45.       textField.style.width = StyleKeyword.Auto;
    46.       textField.style.height = 20;
    47.       rootVisualElement.Add(textField);
    48.  
    49.       enumField = new EnumField("Enum",MyEnum.Foo1);
    50.       enumField.bindingPath = "myEnum";
    51.       enumField.style.width = StyleKeyword.Auto;
    52.       enumField.style.height = 20;
    53.       rootVisualElement.Add(enumField);
    54.      
    55.      
    56.       if (dataObject == null)
    57.       {
    58.          dataObject = CreateInstance<DataObject>();
    59.          dataObject.myString = "default string";
    60.          textField.value = dataObject.myString;
    61.       }
    62.       else
    63.       {
    64.          textField.value = dataObject.myString;
    65.          enumField.value = dataObject.myEnum;
    66.       }
    67.      
    68.      
    69.       var so = new SerializedObject(dataObject);
    70.       //The same thing happens if I try to bind to the field directly instead
    71.       rootVisualElement.Bind(so);
    72.    }
    73. }
    I found this post https://forum.unity.com/threads/enumfield-does-not-work-for-custom-enum-type.678580/#post-4541848
    But they solved the issue on UXML. Is this somehow related to my problem, where I'm trying to do the binding by code? What am I missing? Or is this a bug?
     
  2. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,011
    If you created EnumField from script, you should call EnumField.Init() for handling custom enum type.
     
  3. MacDx

    MacDx

    Joined:
    Feb 1, 2014
    Posts:
    5
    I added the line
    enumField.Init(MyEnum.Foo1);
    right after creating the enum field but the result was the same. Same warning.
     
  4. MacDx

    MacDx

    Joined:
    Feb 1, 2014
    Posts:
    5
    Last edited: Jun 11, 2019
  5. MacDx

    MacDx

    Joined:
    Feb 1, 2014
    Posts:
    5
    I did some digging on the source code and found a couple of things.

    First, here's the decompiled binding code from my current Unity version (2019.1.0f2)

    Code (CSharp):
    1.     private static void DefaultBind<TValue>(VisualElement element, BindingExtensions.SerializedObjectUpdateWrapper objWrapper, SerializedProperty prop, Func<SerializedProperty, TValue> propertyReadFunc, Action<SerializedProperty, TValue> propertyWriteFunc, Func<TValue, SerializedProperty, Func<SerializedProperty, TValue>, bool> valueComparerFunc)
    2.     {
    3.       INotifyValueChanged<TValue> field = element as INotifyValueChanged<TValue>;
    4.       if (field != null)
    5.         BindingExtensions.SerializedObjectBinding<TValue>.CreateBind(field, objWrapper, prop, propertyReadFunc, propertyWriteFunc, valueComparerFunc);
    6.       else
    7.         Debug.LogWarning((object) string.Format("Field type {0} is not compatible with {2} property \"{1}\"", (object) element.GetType().FullName, (object) prop.propertyPath, (object) prop.type));
    8.     }
    So, the field != null check is failing meaning that element as INotifyValueChanged<TValue> is returning null and that's weird since the element is of type EnumField which inherits from BaseField which implements the interface INotifyValueChanged.

    Now here's the source code straight from github for the same method on Unity version 2019.3.0a3

    Code (CSharp):
    1.         private static void DefaultBind<TValue>(VisualElement element, SerializedObjectUpdateWrapper objWrapper, SerializedProperty prop,
    2.             Func<SerializedProperty, TValue> propertyReadFunc, Action<SerializedProperty, TValue> propertyWriteFunc,
    3.             Func<TValue, SerializedProperty, Func<SerializedProperty, TValue>, bool> valueComparerFunc)
    4.         {
    5.             var field = element as INotifyValueChanged<TValue>;
    6.  
    7.             if (element is INotifyValueChanged<string> && typeof(TValue) != typeof(string))
    8.             {
    9.                 //One Way Binding here with string conversions
    10.  
    11.                 SerializedObjectStringConversionBinding<TValue>.CreateBind(element as INotifyValueChanged<string>, objWrapper, prop, propertyReadFunc,
    12.                     propertyWriteFunc, valueComparerFunc);
    13.             }
    14.             else if (field != null)
    15.             {
    16.                 SerializedObjectBinding<TValue>.CreateBind(field, objWrapper, prop, propertyReadFunc,
    17.                     propertyWriteFunc, valueComparerFunc);
    18.             }
    19.             else
    20.             {
    21.                 Debug.LogWarning(string.Format("Field type {0} is not compatible with {2} property \"{1}\"",
    22.                     element.GetType().FullName, prop.propertyPath, prop.type));
    23.             }
    24.         }
    As you can see there's an extra conditional here although it doesn't seem related to this problem since it is checking for the interface with a string type.

    Anybody from Unity that can shed some light on this?
     
    Last edited: Jun 12, 2019
  6. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Hello,

    Support for binding to EnumField to an enum value was added in 2019.3.

    However I cannot see a reason not to do it in 2019.2 and maybe 2019.1. Can you please submit a bug through the bug reporter so you can track that request ?

    Thanks.
     
  7. MacDx

    MacDx

    Joined:
    Feb 1, 2014
    Posts:
    5
    Last edited: Jun 13, 2019