Search Unity

RegisterCallback for ChangeEvent - Dealing with enums

Discussion in 'UI Toolkit' started by Twin-Stick, Jun 10, 2019.

  1. Twin-Stick

    Twin-Stick

    Joined:
    Mar 9, 2016
    Posts:
    111
    Hi all, I am hoping someone can help me out with registering a callback for when an enum field is changed.

    I have tried pretty much all listed control types listed on the UIElements manual with no luck, even tried RegisterCallback<ChangeEvent<EnumField>> and RegisterCallback<ChangeEvent<MyEnumType>>

    I've managed to capture all my other controls but this one has me beat!
     
  2. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Hello,

    That would be
    ChangeEvent<Enum>
    .

    One hint that the API is give you is the
    RegisterValueChangedCallback()
    extension method if the
    INotifyPropertyChange<T> i
    nterface. This should always give an indication of the correct type T that acts as the underlying value of controls.

    Hope this helps.
     
    LuisAlfredo92 likes this.
  3. Twin-Stick

    Twin-Stick

    Joined:
    Mar 9, 2016
    Posts:
    111
    Hi Antoine, thanks for the reply :)
    I had also tried using ChangeEvent<System.Enum> as well as all enumField types I could find - nothing I have tried has worked.
    All other controls I am able to hook into such as strings, ints etc - but any enum fields I can't seem to be able to register a callback.
     
    Last edited: Jun 11, 2019
  4. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Hello,

    One last thing to try if you are using 2019.2 already : from the main menu open "UI > UIElements Samples" and then select "Choice Fields > Enum". This displays an example of using the EnumField and specifically how to register the change event callback.

    If that doesn't help, can you create a simple script that exhibits your issue so we can look into it?

    Thanks
     
    GesunderApfel likes this.
  5. Twin-Stick

    Twin-Stick

    Joined:
    Mar 9, 2016
    Posts:
    111
    Thanks for the reply Antoine.
    Also thankyou for the heads up about the UIElements window, that is very helpful!

    I created a new field and initialized it in the C# script in the same way the example was shown - and success! This worked!

    Previously, I created the field purely in the UXML file via a PropertyField and binding-path. Doing it this way did not register the callback, curiously though - it did for the other controls I have, just not enums.

    Would this be a bug I should report, or just by design?
     
  6. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I'm also struggling with the ChangeEvent. In my case, I'm trying to get a change callback when a PropertyField changes, but it doesn't work:

    Code (CSharp):
    1. var property = SerializedObject.FindProperty("CardSetType");
    2. var propertyField = new PropertyField(property);
    3. propertyField.RegisterCallback<ChangeEvent<System.Enum>>(evt =>
    4. {
    5.     Debug.Log(evt.newValue);
    6. });
    7. root.Add(propertyField);
    I know that I can directly create a dropdown or enum field, but I'd like to handle properties generically, in fact, I don't even need the newValue of the callback, I simply need a callback that something has changed.

    Found Workaround
    After some time I found an answers what to use as a workaround:
    field.RegisterCallback<ChangeEvent<string>>
    works on enum types because the displayed popup is a string dropdown.
     
    Last edited: Jul 9, 2020
    Alverik likes this.
  7. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Xarbrough likes this.
  8. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    Why isn't EnumField using Generics? This just makes everyone's code worse and more error-prone.

    I wrote a wrapper for EnumField that ... does what EnumField is supposed to do, and is typesafe ... haven't seen any problems with it. Is this a design bug in EnumField that we're now stuck with?

    Or is there some subtlety - e.g. something internally in UIToolkit - that breaks when you implement typesafe enums?
     
  9. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    A month later, on a bigger project, I discovered that the answer from @antoine-unity simply cannot work - the Samples don't have any example for EnumField with SerializedProperty, the Sample given only works if you don't need to work with the rest of Unity.

    1. UIToolkit's PropertyField is incompatible with the rest of Unity here: it generates events of a type/value that cannot be passed to SerializedProperty instances (if there's a combo that works, then UIToolkit desperately needs to provide an example, becuase obvious combos break the UnityEditor APIs - see below).
    2. You can fudge this by writing lots of extra code to manually Parse the Enum value and convert it from UIToolkit's wrong type into a type that is compatible with other Unity APIs, but...
    3. To do that you have to register for "RegisterCallback< ChangeEvent<Enum> >" or "RegisterValueChangedCallback<Enum>", but those callbacks are sent AFTER parent objects receive e.g. RegisterCallback<string>, making it impossible to write the value of the Property back into the SerializedProperty in time: all other listeners will receive corrupt/invalid views of data.

    ...this will appear to work up until you write a non-trivial project where you discover that your callbacks are executing in the wrong order - i.e. any real game.

    So far I haven't successfully converted UIToolkit's "who even knows what this is" type into a string that Unity SerializedProperty (and from there: PropertyField) will accept: attempting to do so crashes inside the UnityEditor internal classes with an error "type is not a supported string value" (which shouldn't be true, but it's a bad error message with a typo (!) in it, and this is a Unity core class, so ... beyond my ability to debug).

    As far as I can tell the only workaround is: Don't use EnumField under any circumstances, it's got the wrong API methods and it just doesn't work properly. You have to supply your own implementatio if you want to use Enums and have everything work correctly (bonus: you can implement all the missing Generics code that UIToolkit ought to have here).
     
  10. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,932
    Ah, finally got it!

    1. NEVER register for <ChangeEvent<Enum>> or similar - it's broken, it will ruin your project and you'll spend hours/days/weeks trying to figure out why.
    2. YOU CANNOT register for changes of type "int" because EnumField won't allow it.
    3. YOU MUST register for 'string' changes because (bug!) they take priority over eveyrthing else, even the stronger-typed Enum values (!)
    4. YOU CANNOT (becuase UIToolkit doesn't allow it) register for RegisterValueChangedCallback<string> - it won't even compile because EnumField hasn't been written to allow it.

    ...so we have a situation where EnumField requires us to register for 'string' but also has been written at the source level to prevent us registering for 'string'. Hmm. But...

    5. INSTEAD: use RegisterCallback<ChangeEvent<string>> AND treat the value from UIToolkit as a string BUT then convert it back into what the rest of Unity requires: an int
    6. ... and now everything works correctly.

    i.e.:

    SerializedProperty myProperty = ...
    enumField.RegisterCallback<ChangeEvent<string>>(evt =>
    {
    MyEnumType.TryParse(evt.newValue, out MyEnumType valueInt);
    myProperty.intValue = (int)valueInt;
    myProperty.serializedObject.ApplyModifiedProperties();
    });
     
    venediklee, VGap, CNC_Sai and 4 others like this.
  11. VGap

    VGap

    Joined:
    May 4, 2020
    Posts:
    3

    The solution works as a quick fix. But other problems occur.

    I use SetValueWithoutNotify(Enum) for enumField in the initialisation phase, but in this case ChangeEvent<string> is called.

    I do terrible workaround for this:

    Code (CSharp):
    1. public class Example
    2.     {
    3.         private bool _ignoreEnumCallback;
    4.        
    5.         public void Bind(Data data)
    6.         {
    7.             _ignoreEnumCallback = true;
    8.             _enumFieldType.UnregisterCallback<ChangeEvent<string>>(EnumCallback);
    9.            
    10.             _enumFieldType.SetValueWithoutNotify(data.Type);
    11.  
    12.             _enumFieldType.RegisterCallback<ChangeEvent<string>>(EnumCallback);
    13.         }
    14.        
    15.         private void EnumCallback(ChangeEvent<string> evt)
    16.         {
    17.             if (_ignoreEnumCallback)
    18.             {
    19.                 _ignoreEnumCallback = false;
    20.                 return;
    21.             }
    22.  
    23.             //another code
    24.         }
    25.     }
     
    StephanieRowlinson likes this.
  12. Wilhelm_LAS

    Wilhelm_LAS

    Joined:
    Aug 18, 2020
    Posts:
    53
    Enum fields... Now i face another problem. Other fields sending an initial ChangeEvent at when itself attached to panel. But enum fields DOES NOT! Even you register for AttachToPanel event, the value of any field is coming as null to you. This is such a headache
     
  13. Gavinsta

    Gavinsta

    Joined:
    Oct 12, 2019
    Posts:
    4
    Had to look around to find this, hope it helps someone else.

    On Unity 2022, you can use the RegisterValueChangeCallback like so:
    Code (CSharp):
    1. enumOrPropertyField.RegisterValueChangeCallback((evt) =>
    2.       {
    3.         var newValue = evt.changedProperty.enumValueIndex;
    4.         if (newValue == (int)MyEnum.FirstEntry)
    5.         {
    6.             //evt's enum value = MyEnum.FirstEntry
    7.             ...
    8.         }
    9.     }
    10.  
     
    a436t4ataf likes this.