Bidirectional GUI <-> Data binding, what's the situation in 2017 ?

Hi everyone,

Relatively new to Unity, so forgive my ignorance.
I’m trying to understand the best way to bind variables to GUI elements in Unity 2017. Specifically, how to make sure changes to variables are automatically propagated to the GUI?

e.g. I wish to bind a bool variable in a script to a GUI Toggle.
I can easily use the OnValuedChanged field in the Toggle to change my variable when the GUI is pressed, but what is the best, nicest, cleanest way to have the Toggle display the correct value when my variable is changed outside the GUI? In other GUI frameworks I’ve used there’s always been a way of binding a variable to a GUI element. How is this done in Unity?

I’ve seen the question asked here:
https://forum.unity3d.com/threads/simplifying-ui-data-exchange-method-presented-feedback-requested.288195/#post-1906038
and here:

but perhaps in 2017 there is a simpler solution.

Thanks,
MM

If you only want the UI reflect source changes automatically, you can create your own property notification mechanism. You can use this pattern to update your UI without data binding.

Here is a simple implementation. First, create a base class, this class contains an event and two protected methods.

public abstract class BaseModel
{
    public event Action<object, string> PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged.Invoke(this, propertyName);
        }
    }

    protected bool SetProperty<T>(ref T storage, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
        {
            return false;
        }

        // set new value
        storage = value;

        NotifyPropertyChanged(propertyName);
        return true;
    }
}

You can create your model class by inheriting this BaseModel. Make all fields private and create properties to encapsulate these fields. The PlayerModel class can raise the property change event in the setter by calling the SetProperty method.

public class PlayerModel : BaseModel
{
    private int hp;
    private int mp;
    private long gold;
	
    public int HP
    {
        get { return hp; }
        set { SetProperty(ref hp, value, "HP"); }
    }

    public int MP
    {
        get { return mp; }
        set { SetProperty(ref mp, value, "MP"); }
    }
	
	public long Gold
	{
		get { return gold; }
		set { SetProperty(ref gold, value, "Gold"); }
	}
}

Next, you need to create a controller class. It will register/unregister the PropertyChanged event of PlayerModel, and update the UI control if HP/MP changes.

public class InfoControl : MonoBehaviour
{
    public Text hpText;
    public Text mpText;

    void Start()
    {
        RefreshView();

        // add property changed handlers
        GameState.Instance.player.PropertyChanged += OnPlayerPropertyChanged;
    }

    void OnDestroy()
    {
        // remove property changed handlers
        GameState.Instance.player.PropertyChanged -= OnPlayerPropertyChanged;
    }

    void RefreshView()
    {
        var player = GameState.Instance.player;

        hpText.text = player.HP.ToString();
        mpText.text = player.MP.ToString();
    }

    void OnPlayerPropertyChanged(object sender, string propertyName)
    {
        var player = (PlayerModel)sender;

        if (propertyName == "HP")
        {
            hpText.text = player.HP.ToString();
        }
        else if (propertyName == "MP")
        {
            mpText.text = player.MP.ToString();
        }
    }
}

If you change the “HP” property, e.g. “GameState.Instance.player.HP += 20”. The player object will raise an event with parameter (player, “HP”), and the OnPlayerPropertyChanged method will be called. In this method, you can check the propertyName. If the name matches, then update the text property of hpText by converting the int value to string value.

This example uses the MVC pattern. The InfoControl class is the controller that connects the model class (PlayerModel) to the view. One of the big problems with MVC pattern is that you must create a controller class for each different UI. For example, you need to create a new control class for a UI that displays the HP/MP of enemy instead of player.

If your project is small, it’s okay to use MVC pattern, but if your project gets bigger, using MVC pattern may be painful. You have to create a lot of boilerplate classes to update the UI.

Another way to develop your game UI is using the MVVM pattern. In MVVM pattern, there is no need to create the InfoControl class. The PlayerModel is already support data binding, so you can specify bindings to bind UI.Text to to the “HP”/“MP” property of player. If you want to reuse this UI for enemy, simply duplicate the View and set the binding source to enemy.

*Note that the MVVM pattern can not work without data binding, you need a data binding framework designed for Unity.

If you are interested in data binding, you can check the Peppermint data binding. Peppermint Data Binding is a lightweight data binding framework for Unity. It provides a simple and easy way for Unity games to utilize data binding.

None, that I know of. You need to write a script that sets the variable on the toggle and watches for changes.