For features where the view and its reactive state are tightly coupled, use a self-contained declarative component. This pattern involves inheriting from ViewBase<TState> and defining a nested State class (typically using CommunityToolkit.Mvvm.ComponentModel.ObservableObject).
To compose views that require constructor injection, use ViewFactory.Create<T>(). If you are using Dependency Injection, register UseComponentControlFactory(...) on your AppBuilder.
using Avalonia.Data;
using CommunityToolkit.Mvvm.ComponentModel;
public class CounterComponent() : ViewBase<CounterComponent.State>(new State())
{
public sealed partial class State : ObservableObject
{
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CounterLabel))]
public partial decimal? Counter { get; set; } = 0;
[ObservableProperty]
public partial string StatusText { get; set; } = "Hello world";
public string CounterLabel => $"Counter: {Counter}";
}
protected override object Build(State state) =>
new StackPanel()
.Children(
new TextBlock()
.Text(state, x => x.StatusText),
new TextBlock()
.Text(state, x => x.CounterLabel),
new NumericUpDown()
.Value(state, x => x.Counter, BindingMode.TwoWay),
new Button()
.Content("Increment")
.OnClick(_ => state.Counter++)
);
}