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.
- UI Toolkit Documentation: https://docs.unity3d.com/Manual/UIElements.html
- Runtime Data Binding: https://docs.unity3d.com/Manual/UIE-runtime-binding.html
- UI Builder: https://docs.unity3d.com/Manual/UIBuilder.html
- UI Toolkit Manual Code Examples: https://github.com/Unity-Technologies/ui-toolkit-manual-code-examples
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:
- 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. - 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 - Create visual elements that will make up your UI from C# scripts OR with UI Builder that can bind to your data model.
- 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 supportingMonobehavior
with C# code - 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)
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:- Download the source code from the Garage-Ware Games Public Examples GitHub Repository
- load the TextFieldBindingExample Scene in located in the
DataBinding/TextField
folder - 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.
Note: HTML limitations may cause
<>
to be coppied as its HTML representation and may need to be fixed to correct compiler errors
- Copy the code below into a new C# script called
TextFieldBindingExample.cs
- Create a new GameObject in your scene and attach the
TextFieldBindingExample
script to it. - Make sure you have a UIDocument component on the GameObject and assign a blank UXML document to it.
- 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.
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:- Download the source code from the Garage-Ware Games Public Examples GitHub Repository
- load the ListViewBindingExample Scene in located in the
DataBinding/ListView
folder - 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.
Note: HTML limitations may cause
<>
to be coppied as its HTML representation and may need to be fixed to correct compiler errors
- Copy the code below into a new C# script called
ListViewBindingExample.cs
- Create a new GameObject in your scene and attach the
ListViewBindingExample
script to it. - Make sure you have a
UIDocument
component on theGameObject
and assign a blank UXML document to it. - 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.
Example: DropdownField Bound to a Data Model
Binding a Dropdown Field Visual Element to a data model allows you to create dynamic dropdown menus. Here’s how you can do it:
Option 1:- Download the source code from the Garage-Ware Games Public Examples GitHub Repository
- load the DropdownBindingExample Scene in located in the
DataBinding/DropDown
folder - Enter
PlayMode
and you will see a dropdown with generic data. Pressing space will update the data model with a random number and the binding system will automatically update the values in the list.
Note: HTML limitations may cause
<>
to be coppied as its HTML representation and may need to be fixed to correct compiler errors
- Copy the code below into a new C# script called
DropdownBindingExample.cs
- Create a new GameObject in your scene and attach the
DropdownBindingExample
script to it. - Make sure you have a
UIDocument
component on theGameObject
and assign a blank UXML document to it. - Enter
PlayMode
and you will see a dropdown 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.
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:- Download the source code from the Garage-Ware Games Public Examples GitHub Repository
- load the UnityEventBindingExample Scene in located in the
DataBinding/UnityEvents
folder - 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.
Note: HTML limitations may cause
<>
to be coppied as its HTML representation and may need to be fixed to correct compiler errors
- Copy the code below into a new C# script called
MyEventUi.cs
- Create a new GameObject in your scene and attach the
MyEventUi
script to it. - Make sure you have a
UIDocument
component on theGameObject
and assign a blank UXML document to it. - 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.
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
This section is a part of the W.I.P. version and will be finished soon
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 callingOnPropertyChanged
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:
- Use the Unity Console to log property changes and binding updates.
- Check for null references in your data model and UI elements.
- Use breakpoints to step through your code and verify that bindings are set up correctly.
JetBrains Rider Breakpoint Docs - Visual Studio Breakpoint Docs - Inspect the UI hierarchy in the Unity Editor to ensure that elements are being added as expected.
- Utilize the Unity Profiler to monitor performance and identify any bottlenecks in your data binding logic.
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
orReorderableList
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.