Source Generator, not reflection
Bindings are emitted at compile time as direct calls. No reflection, no boxing, no string lookups per frame.
A field becomes a notifying property. A method becomes a command. Bindings compile to direct calls: zero reflection, zero string lookups.
[ViewModel]
public sealed partial class CounterViewModel
{
[Bind] private int _count;
[RelayCommand]
private void Increment() => Count++;
}
[View]
public sealed partial class CounterView : MonoView
{
[RequireBinder(typeof(int))]
[SerializeField] private MonoBinder[] _count;
[RequireBinder(typeof(IRelayCommand))]
[SerializeField] private MonoBinder[] _incrementCommand;
}
The generator writes the property, the change notification and the command. You keep the field and the method.
public class CounterViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _count;
public int Count
{
get => _count;
set
{
if (_count == value) return;
_count = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Count)));
}
}
public ICommand IncrementCommand { get; }
public CounterViewModel()
{
IncrementCommand = new RelayCommand(() => Count++);
}
}
[ViewModel]
public sealed partial class CounterViewModel
{
[Bind] private int _count;
[RelayCommand]
private void Increment() => Count++;
}
[ViewModel][Bind][RelayCommand][View][RequireBinder]A binder is a MonoBehaviour on the UI element. The generated ViewModel hands it a typed member: no dictionary, no reflection, no string keys at runtime.
Drop a binder on any UI element, pick the ViewModel member and the mode. Converters, formats and fallbacks are serialized fields. Designers rebind without touching C#.
Every design decision starts from the same question: what does this cost at runtime?
Bindings are emitted at compile time as direct calls. No reflection, no boxing, no string lookups per frame.
A field with [Bind] becomes a notifying property. A method with [RelayCommand] becomes a command. Nothing else to write.
OneWay, TwoWay, OneTime and OneWayToSource. The ViewModel sets the upper bound, every binder picks its own mode in the Inspector.
Hundreds of binders for uGUI, TextMeshPro, Transform, Animator, AudioSource and more, plus a catalogue of 190+ converters.
ObservableList, FilteredList and CreateSync keep model and ViewModel lists in step; virtualized lists render only what is visible.
Roslyn analyzers flag a missing partial, a wrong CanExecute signature or a field used where the property was meant, with code fixes.
Import a sample from the Package Manager. Its README is the tutorial.
Install from OpenUPM or the Asset Store, open the Counter sample, press Play.