Version: Unity 6.7 Alpha (6000.7)
Language : English
UxmlAttributeFieldDecorator
Vector2Field

UxmlSerializedDataPropertyDrawer

UxmlSerializedDataPropertyDrawer is the base class for custom property drawers applied to UxmlSerializedData nested classes. When you select a visual element in the Hierarchy window, the Inspector uses this drawer to display and edit the element’s UXML attributes. The base class auto-generates property fields for all properties marked with UxmlAttribute and handles serialized object binding automatically.

Use UxmlSerializedDataPropertyDrawer instead of PropertyDrawer when creating a custom drawer for a type that’s a UxmlObject or a UxmlElement. For plain [Serializable] types that aren’t UxmlObject types, use PropertyDrawer directly.

Apply a custom drawer

Apply the [CustomPropertyDrawer] attribute to your drawer class and target the UxmlSerializedData nested class of the custom control:

using Unity.UIToolkit.Editor;
using UnityEditor;
using UnityEngine.UIElements;

[CustomPropertyDrawer(typeof(MyCustomControl.UxmlSerializedData))]
public class MyCustomControlDrawer : UxmlSerializedDataPropertyDrawer
{
    // The base class generates the full inspector UI automatically.
}

Override points

Override one or both of the following methods to customize the generated UI.

CreateChildPropertiesGUI

Override CreateChildPropertiesGUI to control which properties are shown and in what order. The base implementation generates a field for every [UxmlAttribute] property that isn’t marked with [HideInInspector].

To show or hide attributes by name, call CreateChildPropertiesIncluding or CreateChildPropertiesExcluding instead of iterating the properties yourself. Both take UXML attribute names and keep the default order and property drawers for everything they create:

// Override CreateChildPropertiesGUI to display only specific properties.
[CustomPropertyDrawer(typeof(SelectiveButton.UxmlSerializedData))]
public class SelectiveButtonDrawer : UxmlSerializedDataPropertyDrawer
{
    protected override void CreateChildPropertiesGUI(VisualElement container, SerializedProperty property)
    {
        CreateChildPropertiesIncluding(container, property, "color", "text");
    }
}

CreateChildPropertyGUI

Override CreateChildPropertyGUI to customize how a single property is rendered. The base implementation calls this method for each [UxmlAttribute] property it encounters and creates a UxmlAttributeField for it.

Use this override when a property needs a layout that’s more complex than a single field — for example, pairing a field with action buttons. Wrap any IBindable field in a UxmlAttributeFieldDecorator to retain override and binding affordances. For properties that don’t need custom rendering, call base.CreateChildPropertyGUI to fall back to the default UxmlAttributeField.

The following example targets an InfoPanel element with two string properties. The text property uses a multiline TextField with a Clear button, and the documentationUrl property uses a single-line TextField with an Open button that launches the URL in the system browser:

// Override CreateChildPropertyGUI to render specific properties with custom layouts.
//
// This example targets an InfoPanel element with two string [UxmlAttribute] properties:
//   - "text": a freeform description rendered as a multiline TextField with a Clear button.
//   - "documentationUrl": a URL rendered as a single-line TextField with an Open button.
//
// Both fields use a UxmlAttributeFieldDecorator so they retain override and binding affordances.
// All other properties fall back to the default UxmlAttributeField rendering via the base class.
[CustomPropertyDrawer(typeof(InfoPanel.UxmlSerializedData))]
public class InfoPanelDrawer : UxmlSerializedDataPropertyDrawer
{
    protected override void CreateChildPropertyGUI(VisualElement container, SerializedProperty property, SerializedProperty childProperty)
    {
        switch (childProperty.name)
        {
            case "text":
                container.Add(BuildMultilineFieldWithClear(property, childProperty));
                break;

            case "documentationUrl":
                container.Add(BuildUrlFieldWithOpen(property, childProperty));
                break;

            default:
                base.CreateChildPropertyGUI(container, property, childProperty);
                break;
        }
    }

    // Builds a row with a multiline TextField inside a UxmlAttributeFieldDecorator and a Clear button.
    static VisualElement BuildMultilineFieldWithClear(SerializedProperty property, SerializedProperty childProperty)
    {
        // Cache the path and the object — both properties are references that may be recycled after
        // this method returns, so the button callback must not touch either one.
        string propertyPath = childProperty.propertyPath;
        SerializedObject serializedObject = property.serializedObject;

        VisualElement row = new VisualElement();
        row.style.flexDirection = FlexDirection.Row;

        UxmlAttributeFieldDecorator decorator = new UxmlAttributeFieldDecorator();
        TextField textField = new TextField(childProperty.displayName);
        textField.bindingPath = propertyPath;
        textField.multiline = true;
        textField.style.flexGrow = 1;
        decorator.Add(textField);
        row.Add(decorator);

        row.Add(new Button(() =>
        {
            SerializedProperty serializedProperty = serializedObject.FindProperty(propertyPath);
            if (serializedProperty != null)
            {
                serializedProperty.stringValue = string.Empty;
                serializedObject.ApplyModifiedProperties();
            }
        }) { text = "Clear" });

        return row;
    }

    // Builds a row with a single-line TextField inside a UxmlAttributeFieldDecorator and an Open button.
    static VisualElement BuildUrlFieldWithOpen(SerializedProperty property, SerializedProperty childProperty)
    {
        string propertyPath = childProperty.propertyPath;
        SerializedObject serializedObject = property.serializedObject;

        VisualElement row = new VisualElement();
        row.style.flexDirection = FlexDirection.Row;

        UxmlAttributeFieldDecorator decorator = new UxmlAttributeFieldDecorator();
        TextField textField = new TextField(childProperty.displayName);
        textField.bindingPath = propertyPath;
        textField.style.flexGrow = 1;
        decorator.Add(textField);
        row.Add(decorator);

        row.Add(new Button(() =>
        {
            SerializedProperty serializedProperty = serializedObject.FindProperty(propertyPath);
            if (serializedProperty != null && !string.IsNullOrEmpty(serializedProperty.stringValue))
                Application.OpenURL(serializedProperty.stringValue);
        }) { text = "Open" });

        return row;
    }
}

For a complete working example, refer to Create a custom inventory property drawer.

Additional resources

UxmlAttributeFieldDecorator
Vector2Field