OnEnable/Disable on property drawers

Hello everyone,

I have written a custom inspector for a custom class, and I make use of OnEnable, OnDisable and OnDestroy events to manage some things.

The thing is that now I would like it to not be a MonoBehaviour, in order to make it more versatile and usable. So i dropped the MonoBehaviour inheritance and wrote a custom property drawer for it. So far so good.

The problem comes when I try to do things on OnEnable/Disable/Destroy-like events; which are essential for it to work properly.

Say my custom class is called CC (for which a have a custom property drawer); and there’s another Monobehaviour class named MC attached to a gameobject named GO. MC has a public variable of type CC.

Basically, I want to get notified in the property drawer whenever I select or diselect GO, and whenever MC gets destroyed.

I know there are no such events for a property drawer (tried it), so my question is: Is there anyway I could simulate those events from a property drawer? Any delegate or something?

Thanks in advance.

[CustomPropertyDrawer( typeof( Example ) )]
public class ExamplePropertyDrawer : PropertyDrawer
{
public ExamplePropertyDrawer() : base ()
{
Debug.Log( “ExamplePropertyDrawer Constructor” );
}

	~ExamplePropertyDrawer()
	{
		// Note: This is called by the GC; and not necessarily on inspector selection change.
		Debug.Log( "ExamplePropertyDrawer Destructor" );
	}
}

This is what I’ve been using, seems to be working just fine.

However if you need a solution that fires for absolutely every property drawer individually:

private bool initialized = false;
 
public override void OnGUI( Rect position, SerializedProperty property, GUIContent label )
{
	if ( !initialized )
	Init( property );
}

private void Init( SerializedProperty property )
{
	initialized = true;
}

The best method I could think of would be to use a custom MonoBehaviour (with the ExecuteInEditMode annotation) with the sole purpose of checking for and sending that event.

You’d need to somehow tell if and when the inspector was changed by something in the editor (either directly by the user or by some other script, or even the editor itself), and then you’d need to check to see if the script that the inspector was checking had any components in them that had property drawers associated with them.

There’s likely a much better way to do this, assuming you reverse engineered the internal workings of the Unity Editor, but I wouldn’t suggest that, for a pretty large number of reasons. Not least of which is that it would be horrifically unreadable and hackish.

I wouldn’t worry about making an OnEnable method that works like the one in Editor scripts. Frankly, a single if statement checking to see if you’ve initialized something too expensive to do every frame would be a pretty inexpensive addition to the “OnGUI” or even the “GetPropertyHeight” methods. However, keep in mind that GetPropertyHeight (at least in my experience) is called at least 3 times per OnGUI call, and would be a lot less efficient to be doing checks in.

@Cobo3 Is has been a while. But I am dealing with this issue right now. Did you find an elegant solution for this?

Hello! I know this is an old topic, but I recently faced a similar problem. My goal was to cancel animation tasks for property display. So I created the following custom property drawer class to solve the problem. I hope this code will help someone. ; )

    public abstract class PropertyDrawerWithEvent : PropertyDrawer
    {
        bool init = true;

        ~PropertyDrawerWithEvent()
        {
            Destroy();
        }

        private void PlayModeStateChanged(PlayModeStateChange obj)
        {
            switch (obj)
            {
                case PlayModeStateChange.ExitingEditMode:
                case PlayModeStateChange.ExitingPlayMode:
                    Destroy();
                    break;
            }
        }

        private void SelectionChanged()
        {
            Disable();
        }

        /// <summary>
        /// Write code for when the property is first displayed or redisplayed.
        /// </summary>
        public abstract void OnEnable(Rect position, SerializedProperty property, GUIContent label);

        /// <summary>
        /// Write code for when the property may be hidden.
        /// </summary>
        public abstract void OnDisable();

        /// <summary>
        /// Write code for when the property is destroyed. (e.g. Releasing resources.)
        /// </summary>
        public abstract void OnDestroy();

        public abstract void OnGUIWithEvent(Rect position, SerializedProperty property, GUIContent label);

        public sealed override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            if (init) Enable(position, property, label);

            OnGUIWithEvent(position, property, label);
        }
        
        public void Enable(Rect position, SerializedProperty property, GUIContent label)
        {
            init = false;
            EditorApplication.playModeStateChanged += PlayModeStateChanged;
            Selection.selectionChanged += SelectionChanged;
            OnEnable(position, property, label);
        }

        public void Disable()
        {
            OnDisable();
            EditorApplication.playModeStateChanged -= PlayModeStateChanged;
            Selection.selectionChanged -= SelectionChanged;
            init = true;
        }

        public void Destroy()
        {
            OnDestroy();
            EditorApplication.playModeStateChanged -= PlayModeStateChanged;
            Selection.selectionChanged -= SelectionChanged;
            init = true;
        }
    }