Skip to main content

Bind Modes

Every BindMode on one screen, plus a converter of your own in a binder slot.

You learn: OneTime, OneWay, TwoWay, OneWayToSource; several binders on one member; ITwoWayConverter.

Assumes: Greeter.

Scene: Scenes/Bind Modes.unity. Scripts: Scripts/ViewModels/AudioSettingsViewModel.cs, Scripts/Converters/PerceptualVolumeConverter.cs.

What we build

OneTime 1.0.0
OneWay Normal
TwoWay [────●────] 0.5 [x] Muted
OneWayToSource [ Profile name... ]
[ Reset ]

Four modes, four fields

[ViewModel]
[Serializable]
public sealed partial class AudioSettingsViewModel
{
[OneTimeBind] private readonly string _version = "1.0.0";

[BindAlso(nameof(VolumeLabel))]
[TwoWayBind]
[SerializeField] [Range(0f, 1f)] private float _volume = 0.5f;

[TwoWayBind]
[SerializeField] private bool _isMuted;

[OneWayToSourceBind] private string _profileName;

[RelayCommand]
private void Reset()
{
Volume = 0.5f;
IsMuted = false;
}

private string VolumeLabel => Volume switch
{
0f => "Silent",
< 0.34f => "Quiet",
< 0.67f => "Normal",
_ => "Loud",
};

partial void OnProfileNameChanged(string newValue) =>
Debug.Log($"Profile name is now \"{newValue}\"");
}
MemberModeWho writesBinder in the scene
VersionOneTimenobody after the first readText
VolumeLabelOneWayViewModel → ViewText
VolumeTwoWaybothSlider, Text
IsMutedTwoWaybothToggle, slider interactable
ProfileNameOneWayToSourceView → ViewModelInput field

The mode declared on the ViewModel member is the upper bound. Each binder picks its own mode in the Mode field, but never above what the member allows: a OneWay text on a TwoWay property is fine, a TwoWay slider on a OneWay property is rejected by the editor.

A readonly field becomes OneTime on its own. [OneTimeBind] is spelled out here for clarity.

One member, several binders

Volume is bound twice: the slider (TwoWay) and a label (OneWay). IsMuted is bound twice as well: the toggle (TwoWay) and the slider's interactable through SelectableInteractableMonoBinder with a BoolInvertConverter in its slot. While muted, the slider is disabled.

The ViewModel describes state once. How many UI elements look at it is up to the scene.

Your own converter

[Serializable]
[TypeSelectorDisplay(Group = "Samples/Bind Modes", Name = "Perceptual Volume")]
public sealed class PerceptualVolumeConverter : ITwoWayConverter<float, float>
{
[SerializeField] [Min(0.1f)] private float _exponent = 2f;

public float Convert(float value) => Mathf.Pow(Mathf.Clamp01(value), 1f / _exponent);
public float ConvertBack(float value) => Mathf.Pow(Mathf.Clamp01(value), _exponent);
}
  • [Serializable] lets the converter live in a binder's [SerializeReference] slot.
  • [TypeSelectorDisplay] puts it into the Converter dropdown in the Inspector.
  • ITwoWayConverter is required for a TwoWay binder. Without ConvertBack the slider value would reach the ViewModel unconverted, and the binder warns about it in the console.

It sits in the SliderValueMonoBinder slot: linear volume 0.25 shows as slider position 0.5 and back.

Summary

ConceptWhere
Four BindModesone field per mode
Binder mode ≤ member modethe Mode field of every binder
Several binders per memberVolume, IsMuted
Built-in converterBoolInvertConverter on interactable
Own ITwoWayConverterPerceptualVolumeConverter

More in Binding Modes and Converters.

Next: Stats, commands with a parameter and CanExecute.

Text uses TextMeshPro (part of com.unity.ugui). The sample ships its own font asset in Fonts/ (Liberation Sans, OFL), so it does not depend on the fonts from TMP Essentials.