Data Binding in Unity UI Toolkit Visual Elements

Data binding in Unity UI Toolkit allows you to connect UI Visual Elements to data sources, enabling dynamic updates and interactions. This guide will show you how to use them as we walk you through the basics of setting up data binding from C# and UI Builder in your Unity 6 UIToolkit project.

Understanding Data Binding

Data binding is a technique that allows UI elements to automatically reflect changes in data models. In Unity, this can be achieved using the BindableElement class, which provides a way to bind properties of UI elements also known as visual elements to data sources. This tutorial will start with the binding to elements from your C# scripts so that the data model system can be reinforced with a clear understanding of the related property assignments and attributes. These same technics must be done for any data that you want bound at runtime even when using UI Builder to create UXML based UI documents. later in the tutorial we will cover the same elements using UI builder to create the documents instead of code.

Unity 6 Reference Materials

The following sites were used while I was learning UI Toolkit and in the creation of this tutorial. There are also numerous YouTube video based tutorials that you can follow on UI Toolkit and UI Builder itself and I will eventually make an accompany video for this tutorial and post the link here once it is completed.

Setting Up Data Binding Overview
You MUST be using Unity Version 6.0 or higher for this tutorial

To set up data binding in UI Toolkit, follow these steps:

  1. Create a data model class that holds the properties you want to bind to.
    Note: This can be a standard C# class or a scriptable object, or any other data model that can impliment the needed interfaces.
  2. Implement the INotifyPropertyChanged interface in your data model to notify the UI of changes and setup your parameters with the proper attribute tags.
    Note: You will also need to use the [CreateProperty] and [DontCreateProperty] attributes properly
  3. Create visual elements that will make up your UI from C# scripts OR with UI Builder that can bind to your data model.
  4. Set up bindings in your UI code, linking properties of the UI elements to properties of your data model.
    Note: When using UI Builder you can bind from the UXML or from the supporting Monobehavior with C# code
  5. Once bound, any updates your data model from any sender will automatically reflect these changes in your Runtime UI.

Creating a Data Model that allows Bound Properties

The Data Model is the important first step in this process. Now you may be working from pre-created UXML docs or even existing source code so the parameters needed for the model may already be defined and that is just fine, even if the desired output doesn't really match the data format, that is still fine since the datamodel can and actually should be used as a buffer that translates the data into readily displayable formats for the UI. By following this idea you can better isolate the UI and the data with reusable code while offering performant data access to the UI elements.

In this tutorial we will be creating a few of the most commonly used visual elements and binding there displayed information to the model but that does not mean we need multiple data models, one for each element. In actual use a visual element or one of its properties will essentially be a property of the model. Since UI's have many elements each with many properties and each parameter/property of these individual visual element can be bound to a property in the model.

For Example:
You have a TextField called myTextField, the TextField has a Value property and an Enabled property that is accessed by myTextField.value and myTextField.enabled. The Value can then be bound to a string property to fill it with data, while the Enabled can be bound to a bool property to set its status based on data within the model. 

Of course this is not 100% true as nothing ever is, but for now it will help to clarify the basic use of the binding system. Now this is all standard stuff so far, what make the properties bindable is the [CreateProperty] attribute placed above the public side of the property while adding the [DontCreateProperty] attribute on the private version of the field. Note that when using the proper public with getter and setter tied to a private backer pattern, you MUST add the [CreateProperty] and [DontCreateProperty] attribute above the proper fields or the system will not work. Additionally you can add other needed attributes such as [CreateProperty, HideInInspector] to have access to the data via code but not displayed in the inspector (assuming your datamodel was a scriptable object).

We will start with the DataModel that we will expand upon in later steps by creating a script called MyDataModel.cs in your scripts folder and copy the following code into it:
(Note: you can also download all source code from our Public GitHub Repository)

In the model we have properties that will be used for all of the visual elements in this tutorial with some being actual data values and others being used as controls. We will also need some basic data to populate into the model at startup. For this we are simulating the data load with some static data in the LoadSampleData() method that is called during initialization


Example: Binding a Text Field to your Data Model

Here is a simple example of binding a TextField Visual Element to a data model that you can attach to a blank UI Document in a scene:

Option 1:
  1. Download the source code from the Garage-Ware Games Public Examples GitHub Repository
  2. load the TextFieldBindingExample Scene in located in the DataBinding/TextField folder
  3. Enter PlayMode and you will see a text field with "Hello World" as a value. Pressing space will update the data model with a random number and the binding system will automatically update the value in the text field.
Option 2:
Note: HTML limitations may cause <> to be coppied as its HTML representation and may need to be fixed to correct compiler errors
  1. Copy the code below into a new C# script called TextFieldBindingExample.cs
  2. Create a new GameObject in your scene and attach the TextFieldBindingExample script to it.
  3. Make sure you have a UIDocument component on the GameObject and assign a blank UXML document to it.
  4. Enter PlayMode and you will see a text field with "Hello World" as a value. Pressing space will update the data model with a random number and the binding system will automatically update the value in the text field.

In this example, the TextField is bound to the MyText property of the TextFieldDataModel. When the property changes, the text field updates automatically. Pressing the space bar adds a random number to the data in the data model so you can see the changes happen.


Example: Bind a ListView to a Data Model

Binding a ListView Visual Element to a data model allows you to display collections of data dynamically. Here’s how you can do it:

Option 1:
  1. Download the source code from the Garage-Ware Games Public Examples GitHub Repository
  2. load the ListViewBindingExample Scene in located in the DataBinding/ListView folder
  3. Enter PlayMode and you will see a list with generic values. Pressing space will update the data model with a random number and the binding system will automatically update the values in the list.
Option 2:
Note: HTML limitations may cause <> to be coppied as its HTML representation and may need to be fixed to correct compiler errors
  1. Copy the code below into a new C# script called ListViewBindingExample.cs
  2. Create a new GameObject in your scene and attach the ListViewBindingExample script to it.
  3. Make sure you have a UIDocument component on the GameObject and assign a blank UXML document to it.
  4. Enter PlayMode and you will see a list view with generic data.
    Pressing space will update the data model with a random number and the binding system will automatically update the value's in the list.

In this example, the ListView is bound to the Items property of the ListViewExampleModel. When the items in the list change, the ListView updates automatically. Pressing the Space Bar will up update the list data in the data model and the binding system will update the ListView


Binding to Custom UI Elements and Controls

You can also create custom UI elements that support data binding. To do this, inherit from BindableElement and implement the necessary properties and methods for binding.


using UnityEngine.UIElements;
public class MyCustomElement : BindableElement
{
    private string customText;
    public string CustomText
    {
        get => customText;
        set
        {
            if (customText != value)
            {
                customText = value;
                NotifyPropertyChanged(nameof(CustomText));
            }
        }
    }
    public MyCustomElement()
    {
        // Initialize your custom element here
    }
    public override void OnDataContextChanged()
    {
        // Handle data context changes if necessary
    }
    public override void OnPropertyChanged(string propertyName)
    {
        // Handle property changes if necessary
        base.OnPropertyChanged(propertyName);
    }
}
    

In this example, MyCustomElement is a custom UI element that can be bound to a data model. You can use it just like any other UI element in your Unity project.

Binding to an Event

In addition to binding properties, you can also bind Unity events to your data model. This allows you to trigger actions in response to changes in the data model. In this simple example the text in the button will be set by the model at start and then updated on each click event on the button.

Option 1:
  1. Download the source code from the Garage-Ware Games Public Examples GitHub Repository
  2. load the UnityEventBindingExample Scene in located in the DataBinding/UnityEvents folder
  3. Enter PlayMode and you will see a button saying "Click Me". Pressing the button will update the data model with a random number and the binding system will automatically update the button text.
Option 2:
Note: HTML limitations may cause <> to be coppied as its HTML representation and may need to be fixed to correct compiler errors
  1. Copy the code below into a new C# script called MyEventUi.cs
  2. Create a new GameObject in your scene and attach the MyEventUi script to it.
  3. Make sure you have a UIDocument component on the GameObject and assign a blank UXML document to it.
  4. Enter PlayMode and you will see a button with "Click Me" as a value.
    Clicking the button will update the data model with a random number and the binding system will automatically update the button text.

In this example, the button is bound to the EventMessage property of the MyEventModel. When the button is clicked, the data model updates and logs a message to the console.


Binding to Serialized Objects

You can also bind UI elements to serialized objects, such as ScriptableObjects or MonoBehaviour properties. This allows you to create a more structured data model that can be easily edited in the Unity Editor.


using UnityEngine;
using UnityEngine.UIElements;
using System.ComponentModel;
[CreateAssetMenu(fileName = "MySerializedData", menuName = "Data/MySerializedData")]
public class MySerializedData : ScriptableObject, INotifyPropertyChanged
{
    [SerializeField]
    private string serializedText;
    public string SerializedText
    {
        get => serializedText;
        set
        {
            if (serializedText != value)
            {
                serializedText = value;
                OnPropertyChanged(nameof(SerializedText));
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class MySerializedUI : MonoBehaviour
{
    public MySerializedData serializedData;
    private TextField serializedTextField;
    void Start()
    {
        serializedTextField = new TextField();
        
        // Bind the text field to the serialized data
        serializedTextField.BindProperty(serializedData, nameof(MySerializedData.SerializedText));
        
        // Add the text field to the UI
        var root = GetComponent<UIDocument>().rootVisualElement;
        root.Add(serializedTextField);
        
        // Update the serialized data to see changes in the UI
        serializedData.SerializedText = "Initial Text";
    }
    }
    

In this example, the TextField is bound to the SerializedText property of the MySerializedData ScriptableObject. Changes to the serialized data will automatically update the UI.

How to use UI Builder to Bind Elements with UXML

Handling Updates

To ensure that your UI updates correctly when the data model changes, make sure to call NotifyPropertyChanged in your data model whenever a property is updated. This will trigger the UI to refresh and display the new data.

Best Practices

  • Use INotifyPropertyChanged to notify the UI of changes in your data model.
  • Keep your data model separate from your UI logic to maintain a clean architecture.
  • Use appropriate UI (Visual) Elements for displaying different types of data (e.g., TextField, ListView, etc.).
  • Test your bindings thoroughly to ensure they work as expected.
  • Consider using the Model-View-ViewModel (MVVM) pattern for more complex applications to better manage data binding and UI interactions.

Common Issues

When working with data binding in Unity Visual Elements, you may encounter some common issues:

  • UI Not Updating: Ensure that you are correctly implementing INotifyPropertyChanged and calling OnPropertyChanged when properties change.
  • Binding Errors: Check that the property names used in bindings match those in your data model. Also make sure the the [CreateProperty] and [DontCreateProperty] attributes are properly placed and that if you have a [CreateProperty] on the public variable then there MUST be a [DontCreateProperty] on its related private value.
  • Performance Issues: For large datasets, consider using virtualization techniques to improve performance.
  • Null Reference Exceptions: Ensure that your data model is initialized before binding it to UI elements.

Debugging Tips

When debugging data binding issues in Unity Visual Elements, consider the following tips:

Performance Considerations

When implementing data binding in Unity Visual Elements, it's important to consider performance, especially with large datasets. Here are some general optimization tips to keep in mind:

  • Use ListView or ReorderableList for displaying large collections, as they provide built-in virtualization.
  • Avoid frequent updates to the UI; batch updates when possible to reduce overhead.
  • Minimize the number of bindings in your UI to reduce complexity and improve performance. This is a big one, once you see the power of the system you may be tempted to use a standard 'bind everything' pattern, however this would put a huge strain on the system thus loosing its speed advantages for when its really needed at runtime.
  • Profile your application to identify any performance bottlenecks related to data binding.
  • Consider using asynchronous data loading techniques to keep the UI responsive during data updates.

Advanced Topics

For more advanced data binding scenarios, consider exploring the following topics:

  • Check out our Inventory Window Data Binding Tutorial to see all of these visual elements used together in an interactive runtime form
  • Custom Bindable Elements: Create your own custom UI elements that support data binding.
  • Two-Way Binding: Implement two-way data binding to allow changes in the UI to update the data model and vice versa.
  • Data Validation: Add validation logic to your data model to ensure that only valid data is bound to the UI.
  • Dynamic Data Sources: Bind to dynamic data sources such as databases or web APIs for real-time updates.
  • MVVM Pattern: Implement the Model-View-ViewModel (MVVM) pattern to separate concerns and improve maintainability.

Conclusion

Data binding in Unity Visual Elements provides a powerful way to create dynamic and responsive user interfaces. By following the steps outlined in this guide, you can easily set up data binding in your Unity projects.

Further Reading

For more information on data binding in Unity Visual Elements, check out the official Unity documentation and tutorials on the subject.