Table of Contents

Implement IViewFor<T> by hand and ensure that ViewModel is a DependencyProperty. Also, always dispose bindings via WhenActivated, or else the bindings leak memory. The XAML DependencyProperty system causes memory leaks if you don't use WhenActivated. There's a few rules, but the number one rule is: if you do a WhenAny on anything other than this, then you need to put it inside a WhenActivated. See WhenActivated for details.

The goal in this example is to two-way bind TheText property of the ViewModel to the TextBox and one-way bind TheText property to the TextBlock, so the TextBlock updates when the user types text into the TextBox.

public class TheViewModel : ReactiveObject
{
    private string theText;
    public string TheText
    {
        get => theText;
        set => this.RaiseAndSetIfChanged(ref this.theText, value);
    }
    
    public ReactiveCommand<Unit,Unit> TheTextCommand { get; }

    public TheViewModel()
    {
        TheTextCommand = ReactiveCommand
            .CreateFromObservable(ExecuteTextCommand);
    }

    private IObservable<Unit> ExecuteTextCommand()
    {
        TheText = "Hello ReactiveUI";
        return Observable.Return(Unit.Default);
    }
}
<Window /* snip */>
  <StackPanel>
    <TextBox x:Name="TheTextBox" />
    <TextBlock x:Name="TheTextBlock" />
    <Button x:Name="TheTextButton" />
  </StackPanel>
</Window>

In this example we are implementing the IViewFor<TViewModel> interface by hands, but for a UserControl we would use a ReactiveUserControl<TViewModel> that encapsulates the IViewFor implementation.

public partial class TheView : Window, IViewFor<TheViewModel>
{
    public static readonly DependencyProperty ViewModelProperty = DependencyProperty
        .Register(nameof(ViewModel), typeof(TheViewModel), typeof(TheView));
        
    public TheView()
    {
        InitializeComponent();
        ViewModel = new TheViewModel();
        
        // Setup the bindings
        // Note: We have to use WhenActivated here, since we need to dispose the
        // bindings on XAML-based platforms, or else the bindings leak memory.
        this.WhenActivated(disposable =>
        {
            this.Bind(this.ViewModel, x => x.TheText, x => x.TheTextBox.Text)
                .DisposeWith(disposable);
            this.OneWayBind(this.ViewModel, x => x.TheText, x => x.TheTextBlock.Text)
                .DisposeWith(disposable);
            this.BindCommand(ViewModel, x => x.TheTextCommand, x => x.TheTextButton)
                .DisposeWith(disposable);
        });
    }

    public TheViewModel ViewModel
    {
        get => (TheViewModel)GetValue(ViewModelProperty);
        set => SetValue(ViewModelProperty, value);
    }

    object IViewFor.ViewModel
    {
        get => ViewModel;
        set => ViewModel = (TheViewModel)value;
    }
}