Version: 2021.3
Create a custom style for a custom control
Bindable elements reference

SerializedObject data binding

Data bindings synchronize properties of non-UI object, such as a string property on a MonoBehaviour, with properties of UI objects, such as the value property of a TextField. A binding refers to the link between the property and the visual control that modifies it.

Use data binding to synchronize values between a property and a specific visual element, so you don’t need to write event handlers when the value changes in the UI.

Note: SerializedObject data binding only works in the Editor, not at runtime.

Serialization requirements

You can only bind to serialized properties. This means you can only bind visual elements to the following objects that are compatible with the Serialization system:

  • User-defined ScriptableObject classes
  • User-defined MonoBehaviour classes
  • Native Unity component types
  • Native Unity asset types
  • Primitive C# types such as int, bool, float, and so on.
  • Native Unity types such as Vector3, Color, Object, and so on.

Value binding

You can only bind the value property of visual elements that implement the INotifyValueChanged interface. For example, you can bind TextField.value to a string, but you can’t bind TextField.name to a string.

You can bind between an object and any visual element that either derives from BindableElement or implements the IBindable interface.

Create a binding

To create a binding, either call Bind() or BindProperty().

Call Bind()

You can call Bind() to bind an element to a SerializedObject. Before you bind an element, you must set the binding path and create a SerializedObject.

Use this method if you don’t have easy access to the SerializedProperty for the binding. See Create a binding with a C# script for an example.

The Bind() extension method sets up an entire hierarchy of visual elements with specified bindingPath properties. You can call the Bind() method on a single element or the parent of the hierarchy that you want to bind. For example, you can call Bind() on the rootVisualElement of an Editor window. This binds all child elements with specified bindingPath properties.

Don’t call Bind() from the Editor.CreateInspectorGUI() or PropertyDrawer.CreatePropertyGUI() override. These overrides are called automatically on the visual elements that these methods return.

Call Unbind()

The Unbind() method stops the value tracking for the element and all its direct and indirect child elements. In general, you don’t need to call Unbind() because tracking stops when a user closes the Inspector or Editor window. Call Unbind() if you must bind elements to different targets in their lifetimes.

If you construct an InspectorElement in C# by calling its constructor, binding occurs during the constructor call. If you want to rebind an InspectorElement after it has been constructed, you must call Unbind() and then either call Bind() explicitly or let a bind operation from a parent create a binding.

Set binding path

If you call Bind() to create the binding, you must set the visual element’s binding path to the property name of the object that you want to bind to.

例如:

  • If you have the following component script:

    using UnityEngine;
    
    public class MyComp : MonoBehaviour
    {
        [SerializeField]
        int m_Count;
    }
    

    To bind your visual element to m_Count, set the binding path to m_Count.

  • If you want to bind a visual element to a GameObject’s name property, which is m_Name, set the binding path to m_Name.

You can set the binding path in UI Builder, UXML, or with a C# script:

Call BindProperty()

You can call BindProperty() to bind an element to a SerializedProperty directly.

Use this method if you already have a SerializedProperty object, and especially if you traverse the properties of a SerializedObject to build a UI dynamically. See Bind without the binding path for an example.

Bind elements to nested properties

You can bind a visual element to nested properties in the source object. To do so, combine the binding path of an element with the binding path of the first ancestor. Use this method with the following elements:

See Bind to nested properties for an example.

Receive callbacks when values change

You can create a binding to receive a callback when a bound serialized property changes. To do so, leverage the TrackPropertyValue() extension method, which is available to any VisualElement. This registers a callback that executes when the provided SerializedProperty changes. See Receive callbacks when a serialized property changes for an example.

You can also create a binding to receive a callback when any properties of the bound serialized object changes. To do so, leverage the TrackSerializedObjectValue() extension method, which is available to any VisualElement. This registers a callback that executes when the provided SerializedProperty changes. See Receive callbacks when any properties change for an example.

Bind custom elements

You can create custom elements and bind them to serialized properties through the value binding system.

To create bindable custom elements:

  1. Declare a custom element.
  2. Inherit the element from BindableElement or implement the IBinding interface.
  3. Implement the INotifyValueChanged interface.
  4. Implement the SetValueWithoutNotify() method to the INotifyValueChanged interface.
  5. Implement the value property accessors to the INotifyValueChanged interface.

See Create and style a custom control for an example.

When writing a custom inspectors you don’t have to explicitely bind a serialized object to the visual tree like in the editor windows examples above. This step is done implicitely after the CreateInspectorGUI method finishes. This automatic binding step is only performed at that time. If you add a field outside of a callback to the CreateInspectorGUI method, you will need to bind it manually. Otherwise it might fail to render or produce undefined visual results.

最佳实践

Based on the type of UI you create, binding occurs at various times. This is called bind time.

The following table describes the bind time of a control:

Condition Automatic bind time (assuming binding path was set)
An InspectorElement constructed in C# During the constructor call
A child element that is under the return value of CreateInspectorGUI() or CreatePropertyGUI() when those methods return After CreateInspectorGUI() or CreatePropertyGUI() returns
A child element that is under an element when Bind() or BindProperty() is called on the parent element During the Bind() or BindProperty() call
Other No automatic binding; you must bind the element or one of its parents manually

The following are best practices when creating a binding regarding bind time:

  • If you create a custom Editor or custom PropertyDrawer, set the elements’ binding paths instead of calling Bind() or BindProperty() on any visual elements that are in the visual tree by the end of the body of CreateInspectorGUI() or CreatePropertyGUI(). These elements are bound automatically after CreateInspectorGUI() or CreatePropertyGUI() returns. However, if you add any elements to the visual tree after that point, call Bind() or BindProperty() to bind them.
  • If you create any other type of UI, call Bind() or BindProperty() regardless of the time at which the elements get added to the visual tree. If you call Bind() or BindProperty() and bind multiple controls at the same time, set the binding path of each control and then call Bind() on the lowest-level parent element that encompasses all the controls. Bind() binds the element on which it’s called if it has a binding path and recursively binds all its child elements if they have binding paths. To prevent a negative performance impact, don’t bind a visual element with the Bind() method more than once.

其他资源

Create a custom style for a custom control
Bindable elements reference