Version: 2020.1
Built-in Controls
Supporting IMGUI

Bindings

The purpose of bindings is to synchronize properties within objects to the visible UI. A binding refers to the link between the property and the visual control that modifies it.

Binding is done between an object and any UIElement that either derives from BindableElement or implements the IBindable interface.

From the UnityEditor.UIElements namespace:

Base Class:

  • BaseCompositeField
  • BasePopupField
  • CompoundFields
  • TextValueField

Controls:

  • InspectorElement
  • ProgressBar
  • BoundsField
  • BoundsIntField
  • ColorField
  • CurveField
  • DoubleField
  • EnumField
  • FloatField
  • GradientField
  • IntegerField
  • LayerField
  • LayerMaskField
  • LongField
  • MaskField
  • ObjectField
  • PopupField
  • PropertyControl
  • RectField
  • RectIntField
  • TagField
  • Vector2Field
  • Vector2IntField
  • Vector3Field
  • Vector3IntField
  • Vector4Field

From the UnityEngine.UIElements namespace:

Base Class

  • BaseField
  • BaseSlider
  • TextInputBaseField
  • TemplateContainer

Controls

  • Foldout
  • MinMaxSlider
  • Slider
  • SliderInt
  • TextField
  • Toggle

Binding is done by following these steps while using a Control from one of the namespaces listed above.

  1. In the Control, specify the bindingPath from the IBindable interface so the UI knows which property to bind. You can do this in C# or in UXML. An example of each is provided later in this topic.
  2. Create a SerializedObject for the object being bound.
  3. Bind this object to the Control or one of its parents.

Binding with C#

The following code snippet shows how to create a binding with C# code. To use this snippet, save this example as a C# file in an editor folder, in your project. Name the C# file SimpleBindingExample.cs.

The contents of SimpleBindingExample.cs:

using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;

namespace UIElementsExamples
{
   public class SimpleBindingExample : EditorWindow
   {
       TextField m_ObjectNameBinding;
      
       [MenuItem("Window/UIElementsExamples/Simple Binding Example")]
       public static void ShowDefaultWindow()
       {
           var wnd = GetWindow<SimpleBindingExample>();
           wnd.titleContent = new GUIContent("Simple Binding");
       }
    
       public void OnEnable()
       {
           var root = this.rootVisualElement;
           m_ObjectNameBinding = new TextField("Object Name Binding");
           m_ObjectNameBinding.bindingPath = "m_Name";
           root.Add(m_ObjectNameBinding);
           OnSelectionChange();
       }
    
       public void OnSelectionChange()
       {
           GameObject selectedObject = Selection.activeObject as GameObject;
           if (selectedObject != null)
           {
               // Create serialization object
               SerializedObject so = new SerializedObject(selectedObject);
               // Bind it to the root of the hierarchy. It will find the right object to bind to...
               rootVisualElement.Bind(so);
    
               // ... or alternatively you can also bind it to the TextField itself.
               // m_ObjectNameBinding.Bind(so);
           }
           else
           {
               // Unbind the object from the actual visual element
               rootVisualElement.Unbind();
              
               // m_ObjectNameBinding.Unbind();
              
               // Clear the TextField after the binding is removed
               m_ObjectNameBinding.value = "";
           }
       }
   }
}

In Unity, select Window > UIElementsExamples > Simple Binding Example. You can use this window to select any GameObject in your scene and modify its name with the TextField shown.

Binding with UXML

This section shows how to use binding through the UXML hierarchy set-up.

In UXML, the attribute binding-path is defined in the TextField control. This is what binds the control to the effective property of the object.

The contents of SimpleBindingExample.uxml:

<UXML xmlns:ui="UnityEngine.UIElements">
 <ui:VisualElement name="top-element">
   <ui:Label name="top-label" text="UXML-Defined Simple Binding"/>
   <ui:TextField name="GameObjectName" label="Name" text="" binding-path="m_Name"/>
 </ui:VisualElement>
</UXML>

The contents of SimpleBindingExample.cs:

using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;


namespace UIElementsExamples
{
   public class SimpleBindingExampleUXML : EditorWindow
   {
       [MenuItem("Window/UIElementsExamples/Simple Binding Example UXML")]
       public static void ShowDefaultWindow()
       {
           var wnd = GetWindow<SimpleBindingExampleUXML>();
           wnd.titleContent = new GUIContent("Simple Binding UXML");
       }

       public void OnEnable()
       {
           var root = this.rootVisualElement;
           var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Editor/SimpleBindingExample.uxml");
           visualTree.CloneTree(root);
           OnSelectionChange();
       }
    
       public void OnSelectionChange()
       {
           GameObject selectedObject = Selection.activeObject as GameObject;
           if (selectedObject != null)
           {
               // Create serialization object
               SerializedObject so = new SerializedObject(selectedObject);
               // Bind it to the root of the hierarchy. It will find the right object to bind to...
               rootVisualElement.Bind(so);
           }
           else
           {
               // Unbind the object from the actual visual element
               rootVisualElement.Unbind();
              
               // Clear the TextField after the binding is removed
               // (this code is not safe if the Q() returns null)
               rootVisualElement.Q<TextField>("GameObjectName").value = "";
           }
       }
   }
}

Using bindings within InspectorElement

An InspectorElement is the UIElement counterpart of an inspector that is meant a specific type of Unity object. Using an InspectorElement to inspect objects gives the following advantages :

  • Creates the UI.
  • Automatically binds objects and the UI.

Another simple binding example, found under Assets/Editor/SimpleBindingExample.cs, provides a usage example and an overview of the process.

The contents of Assets/Editor/SimpleBindingExample.cs:

using UnityEditor;
using UnityEngine;
using UnityEditor.UIElements;

namespace UIElementsExamples
{
   public class SimpleBindingExampleUXML : EditorWindow
   {
       [MenuItem("Window/UIElementsExamples/Simple Binding Example UXML")]
       public static void ShowDefaultWindow()
       {
           var wnd = GetWindow<SimpleBindingExampleUXML>();
           wnd.titleContent = new GUIContent("Simple Binding UXML");
       }

       TankScript m_Tank;
       public void OnEnable()
       {
           m_Tank = GameObject.FindObjectOfType<TankScript>();
           if (m_Tank == null)
               return;
    
           var inspector = new InspectorElement(m_Tank);
           rootVisualElement.Add(inspector);
       }
   }
}

This code references the TankScript script and uses the InspectorElement. The TankScript script is an example of a MonoBehaviour assigned to a GameObject.

The contents of Assets/TankScript.cs:

using UnityEngine;

[ExecuteInEditMode]
public class TankScript : MonoBehaviour
{
   public string tankName = "Tank";
   public float tankSize = 1;

   private void Update()
   {
       gameObject.name = tankName;
       gameObject.transform.localScale = new Vector3(tankSize, tankSize, tankSize);
   }
}

The InspectorElement is customized with a specific UI. This is done with the TankEditor script. The TankEditorscript defines a custom editor for the TankScript type. The TankEditorscript also uses a UXML file for the hierarchy and a USS file to style the inspector.

The contents of Assets/Editor/TankEditor.cs:

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

[CustomEditor(typeof(TankScript))]
public class TankEditor : Editor
{
   public override VisualElement CreateInspectorGUI()
   {
       var visualTree = Resources.Load("tank_inspector_uxml") as VisualTreeAsset;
       var uxmlVE = visualTree.CloneTree();
uxmlVE.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Resources/tank_inspector_styles.uss"));
      return uxmlVE;
   }
}

The contents of Assets/Resources/tank_inspector_uxml.uxml:

<UXML xmlns:ui="UnityEngine.UIElements" xmlns:ue="UnityEditor.UIElements">
   <ui:VisualElement name="row" class="container">
       <ui:Label text="Tank Script - Custom Inspector" />
       <ue:PropertyField binding-path="tankName" name="tank-name-field" />
       <ue:PropertyField binding-path="tankSize" name="tank-size-field" />
   </ui:VisualElement>
</UXML>

The UXML file, tank_inspector_uxml.uxml specifies the binding. Specifically, each binding-path attribute, for each the PropertyFields tag, is set to the property to bind. What element is shown in the UI is based on the type of each bound property.

The contents of Assets/Resources/tank_inspector_styles.uss:

.container {
   background-color: rgb(80, 80, 80);
   flex-direction: column;
}
Label {
   background-color: rgb(80, 80, 80);
}
TextField:hover {
   background-color: rgb(255, 255, 0);
}
FloatField {
   background-color: rgb(0, 0, 255);
}

The USS file, tank_inspector_styles.uss, defines the style of each element.

The following table lists the fields supported by the PropertyField. Each field includes its data type.

Field Type
BoundsField Bounds
BoundsIntField BoundsInt
ColorField Color
CurveField AnimationCurve
FloatField float
GradientField Gradient
IntegerField целое число (int)
IntegerField int, for the ArraySize
LayerMaskField unit
ObjectField UnityEngine.Object
PopupField<string> Enum
RectField Rect
RectIntField RecInt
TextField строка
TextField, with a maxLength=1 char
Toggle bool
Vector2Field Vector2
Vector2IntField Vector2Int
Vector3Field Vector3
Vector3IntField Vector3Int
Vector4Field Vector4
Built-in Controls
Supporting IMGUI