Skip to content

9.3. WPF Integration

Wire a ComponentVM<M> to a WPF view via INotifyPropertyChanged and the existing RelayCommand infrastructure. WPF is Windows-only; for cross-platform XAML, see avalonia.md.

9.3.1. Reactivity primitive

WPF data binding observes INotifyPropertyChanged.PropertyChanged events. VMx VMs publish PropertyChangedMessage<TSender>(sender, senderName, propertyName) to their IMessageHub instead. The adapter translates one to the other.

9.3.2. Mapping

WPF VMx
INotifyPropertyChanged PropertyChangedMessage<T> on IMessageHub
ICommand RelayCommand / RelayCommand<T> (already implements ICommand)
INotifyCollectionChanged CollectionChangedMessage on the hub
Dispatcher.Invoke IDispatcher.Foreground scheduler

9.3.3. Adapter skeleton

public sealed class BindableVm<M> : INotifyPropertyChanged, IDisposable
{
    private readonly ComponentVM<M> _vm;
    private readonly IDisposable _sub;

    public BindableVm(ComponentVM<M> vm, IMessageHub hub)
    {
        _vm = vm;
        _sub = hub.Messages
            .OfType<IPropertyChangedMessage<IComponentVM>>()
            .Where(m => ReferenceEquals(m.Sender, vm))
            .Subscribe(m => PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(m.PropertyName)));
    }

    public M Model { get => _vm.Model; set => _vm.Model = value; }
    public string Name => _vm.Name;
    public ConstructionStatus Status => _vm.Status;

    public event PropertyChangedEventHandler? PropertyChanged;
    public void Dispose() => _sub.Dispose();
}

Then in XAML: <TextBlock Text="{Binding Model.Title}"/> and <Button Command="{Binding SaveCommand}"/> where SaveCommand is a RelayCommand exposed by your domain wrapper.

9.3.4. Fuller example

examples/csharp/wpf/TodoApp/ — a working WPF Todo app demonstrating RelayCommand + IMessageHub with per-item ComponentVM<TodoItem> children in an ObservableCollection.