Core Concepts
What is QfStudio.Godette.ReactiveUI
ReactiveUI is a composable, cross-platform MVVM (Model-View-ViewModel) framework for .NET. It uses reactive extensions to bind UI elements to ViewModel properties and commands, keeping views and business logic cleanly separated.
QfStudio.Godette.ReactiveUI provides the platform services that make ReactiveUI work with Godot Engine - scheduling, activation, property-change notification, and command binding. If you have used ReactiveUI with Avalonia or WPF, this is the same this.Bind / this.BindCommand / WhenActivated story, now wired to Godot nodes and signals.
Activation Semantics
A view is activated (true) when its Godot Node is inside the scene tree and IsNodeReady() returns true. It deactivates (false) on TreeExited. This is semantically equivalent to Avalonia's AttachedToVisualTree / DetachedFromVisualTree.
How activation is detected
The GodotActivationFetcher emits true from three paths:
- the
Readysignal (first entry, all children initialized); TreeEntered+IsNodeReady()(re-entry after the node was already ready);- an initial
IsInsideTree() && IsNodeReady()check at subscription time.
It emits false on TreeExited.
Note: the C# virtual method _Ready runs before the Ready signal is emitted, so a ViewModel assigned in _Ready is already set by the time WhenActivated fires.
usings
All examples in this guide assume these usings are in scope:
using QfStudio.Godette.ReactiveUI;
using ReactiveUI;
using System.Reactive.Disposables; // for DisposeWith(d) used throughout[GodotViewFor<T>] and the generated ViewModel property are emitted by this library's bundled source generator into the QfStudio.Godette.ReactiveUI namespace -- no extra using or package reference is required.
Basic Setup
A ViewModel implements IActivatableViewModel. A View uses the [GodotViewFor<T>] source generator attribute to implement IViewFor<T>. Bind in the constructor inside WhenActivated:
// ViewModel
public partial class MyViewModel : ReactiveObject, IActivatableViewModel
{
public ViewModelActivator Activator { get; } = new();
[Reactive] public partial string Name { get; set; } = "";
}
// View (.tscn root script)
[GodotViewFor<MyViewModel>]
public partial class MyScene : Control
{
public MyScene()
{
this.WhenActivated(d =>
{
this.Bind(ViewModel, vm => vm.Name, v => v.NameEdit.Text)
.DisposeWith(d);
});
}
public override void _Ready()
{
ViewModel = new MyViewModel();
}
}Why assign ViewModel in _Ready?
In Godot there is no built-in UI/routing framework that creates views and injects their ViewModel for you. Unlike Avalonia + ReactiveUI, where RoutingState and the platform's IViewLocator typically construct the View and set ViewModel for you during navigation, in Godot each scene's root script must instantiate its own ViewModel at some point. The recommended place is _Ready, because:
- Godot guarantees
_Readyis called after all children are initialized, so[SceneTree]-generated node properties (e.g.NameEdit) are non-null here; - Godot's C# virtual
_Readyruns before theReadysignal is emitted, andWhenActivatedsubscribes to theReadysignal viaGodotActivationFetcher, so_Ready'sViewModel = new MyViewModel();completes before theWhenActivatedcallback fires.
If you wire navigation yourself (see Routing), the RoutedViewController sets view.ViewModel = viewModel after resolving the view, so you don't need _Ready assignment for routed views -- only for top-level/root scenes.