Property Drawer
Property Drawers are used to draw any field of a certain type with a custom VisualElement or IMGUI in all inspectors.
You can learn more about PropertyDrawers reading the documentation:
Since I focus mainly of VisualElements, I will explain a few tricks I use with that.
To avoid the issue mentioned above I prefer writing my property drawers like so:
1[CustomPropertyDrawer( typeof(MyClass) )]2public class MyClassDrawerUIE : PropertyDrawer3{4 public override VisualElement CreatePropertyGUI( SerializedProperty property )5 {6 var container = new View( property );7 return container;8 }9
10 private class View : VisualElement11 {12 private SerializedProperty m_SerializedProperty;13
14 public OptionalBlendCurveVisualElement ( SerializedProperty property )15 {16 m_SerializedProperty = property;17 // Create property container element.18 var container = new VisualElement();19 Add(container);20
21 //...Add stuff to my container here.22 }23 }24}This ensures I get a unique view object for each property and cache the property in there.
Get context of the view
If I needed to get a parent VisualElement to get some context for my drawer I can now do so by checking the GeometryChangedEvent.
1private class View : VisualElement2{3 private SerializedProperty m_SerializedProperty;4
5 public OptionalBlendCurveVisualElement ( SerializedProperty property )6 {7 m_SerializedProperty = property;8 // Create property container element.9 var container = new VisualElement();10 Add(container);11
12 RegisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );13
14 //...Add stuff to my container here.15 }16
17 private void OnGeometryChangedEvent( GeometryChangedEvent evt )18 {19 var parentVisualElement = GetFirstAncestorOfType<MyParentVisualElement>();20 //... Get the values you need from the parent visualElement like a custom Inspector or Editor Window.21 }22}There are other interesting events you can listen to. Most of them can be found on this documentation page.
Get the value of the Serialized Property
In many cases when using Property Drawers you’ll want to get the actual value of the Property you are drawing. Unity does not make this particularly easy.
So here are some static functions I recommend you add in a static utility class:
1public static SerializedProperty GetParentProperty(SerializedProperty property)2{3 var propertyParentParentPath =4 property.propertyPath.Substring(0, property.propertyPath.LastIndexOf("."));5 return property.serializedObject.FindProperty(propertyParentParentPath);6}7
8public static object GetSerializedPropertyParent(UnityEditor.SerializedProperty prop)9{10 return GetSerializedPropertyValue(prop, 1);11}12
13public static object GetSerializedPropertyValue(UnityEditor.SerializedProperty prop, int inverseDepth = 0)14{15 var path = prop.propertyPath.Replace(".Array.data[", "[");16 object obj = prop.serializedObject.targetObject;17 var elements = path.Split('.');18 for (int i = 0; i < elements.Length; i++) {19 if (elements.Length - i < inverseDepth) { return obj; }20 string element = elements[i];21 if (element.Contains("[")) {22 var elementName = element.Substring(0, element.IndexOf("["));23 var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "")24 .Replace("]", ""));25 obj = GetPropertyFieldValue(obj, elementName, index);26 } else { obj = GetPropertyFieldValue(obj, element); }27 }28 return obj;29}30
31public static object GetPropertyFieldValue(object source, string name)32{33 if (source == null) { return null; }34 var type = source.GetType();35 var f = type.GetField(name,36 System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public |37 System.Reflection.BindingFlags.Instance);38
39 if (f == null) {40 var p = type.GetProperty(name,41 System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public |42 System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);43 if (p == null) { return null; }44 return p.GetValue(source, null);45 }46 return f.GetValue(source);47}48
49public static object GetPropertyFieldValue(object source, string name, int index)50{51 var enumerable = GetPropertyFieldValue(source, name) as IEnumerable;52 var enm = enumerable.GetEnumerator();53 while (index-- >= 0)54 enm.MoveNext();55 return enm.Current;56}Update the view when the property changes
Finally some additional extension functions that are quite useful to know about are the Binding Extensions. Used to Bind and keep track of the serialized object and properties. It’s particularly useful to know when the property has changed and the visual Element should be updated to reflect that change.
1private class View : VisualElement2{3 private SerializedProperty m_SerializedProperty;4
5 public OptionalBlendCurveVisualElement ( SerializedProperty property )6 {7 m_SerializedProperty = property;8 // Create property container element.9 var container = new VisualElement();10 Add(container);11
12 TrackPropertyValue( property, OnValueChanged );13
14 //...Add stuff to my container here.15 }16
17 private void OnValueChanged( SerializedProperty property )18 {19 // The Bound property has changed.20 //...Refresh your Visual Elements21 }22}