Custom PropertyAttribute with Custom Serilazable class

Hi, I want to use my custom serializable class with custom property attributes, like this :

[TestProperty("hi")]
public TestSerializableClass test_class;

But if I do this, it seems like unity doesn’t call Custom propertydrawer for serialized class.

I made a test codes to simplify my problems -

  1. Test Serializable Class
[Serializable]
public class TestSerializableClass
{
    public string hi;
    public string hi_hidden;
}
  1. Custom PropertyDrawer for Test Class
[CustomPropertyDrawer(typeof(TestSerializableClass), true)]
public class TestSerializableClassDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        EditorGUI.PropertyField(position, property.FindPropertyRelative("hi"));
        EditorGUI.EndProperty();
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return base.GetPropertyHeight(property, label) * 3f;
    }
}
  1. Custom Property Attribute
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property |
    AttributeTargets.Class | AttributeTargets.Struct, Inherited = true)]
public class TestPropertyAttribute : PropertyAttribute
{
    public string hi;

    public TestPropertyAttribute(string hi)
    {
        this.hi = hi;
    }
}
  1. Custom Property Attribute Drawer
[CustomPropertyDrawer(typeof(TestPropertyAttribute))]
public class TestPropertyAttributeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        TestPropertyAttribute attr = (TestPropertyAttribute)attribute;
    
        EditorGUI.PropertyField(position, property, label, true);
    }
    //Also the height is messed up, anyway overriden to debug show what's going on.
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return base.GetPropertyHeight(property, label) * 3;
    }
}

So there are two property drawers : one for the attribute, one for the serializable class.

And during the process, I realized that only custom drawer for attribute is called when I use both together. That’s the reason I used EditorGUI.PropertyField() in line 8, to use PropertyDrawer also for serializable class.
But it seems like this line doesn’t call my custom propertydrawer!

Here’s my test script :

[TestProperty("Hi")]
    public TestSerializableClass test;

    [Space(10)]
    public TestSerializableClass test_two;

Here’s how it looks :
3254906--250722--upload_2017-10-16_13-56-53.png

So, how can I use my custom propertydrawer for my serialized class, not the unity default propertydrawer?

Thanks for your answer in advance!

You can’t use both drawers at the same time. Drawers aren’t stacked or nested together (likely cause one drawer can be destructive on another, ex. layout scopes), instead Unity grabs the best drawer available for the object and uses that. IIRC for fields, it checks for relevant Decorator drawers first, and if it hasn’t found one it tries to find the most relevant Property Drawer (if one exists), if it can’t find that it falls back to Unity’s own default property drawer.

As for your “GetPropertyHeight” issue in your propertyDrawer, thats a known issue where the base class doesn’t account for the “isExpanded” attribute on a Serialized Property. You can use this instead.

        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return EditorGUI.GetPropertyHeight(property,label,true);
        }

You could hack this together by looking (through reflection) for other defined property drawers, and then calling them inside of your TestPropertyAttributeDrawer. That’s pretty cumbersome, though!

What’re you trying to do? If you want to add some extra information, you can use a DecoratorDrawer, which draws extra stuff over a property (like the built-in Header or Space attributes)

@JoshuaMcKenzie Thanks for your answer, good to know what’s going on behind the curtain.

@Baste I wanted to make an attribute that can be used globally for any class or structs, something like [ConditionalHide(“target_condition_field”)] and to do this, I needed to use EditorGUI.PropertyField because I cannot know what field type I’m messing with.

So about the reflection thing you mentioned, do you mean that there’re something like EditorGUI.CreateEditor() for PropertyDrawer? How can I use it? Can I have an example for this? Thanks!

No, I was thinking that you’d find everything that inherits from PropertyDrawer, check if their CustomPropertyDrawer-attribute matches your current class, and if that’s the case, instantiate an instance of that drawer and call OnGUI/GetPropertyHeight for that type.

I’ve done exactly the same thing as you have (HideIf attribute) and I’ve just simply not bothered to solve the problem. I probably should!

Hey, just remembered that I made a github repo with my HideIf things way back.

I added support for CustomPropertyDrawers. Check the repo here. The stuff relating to your question is all in HideIfAttributeDrawer, and is basically what I explained in my last post.

Wow, hey that’s quite more complex than I thought. But anyway, your code helped me a lot to understand propertydrawer. Thanks mate!