Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
497 lines (430 sloc) 27.9 KB

Order: 10

ReactiveUI is built on a reactive programming foundation and specifically implemented utilizing Reactive Extensions. Observables, asynchronous data streams, are core to reactive programming and what makes ReactiveUI impressive. Dealing with Observables is a large part of working with ReactiveUI. Observables can represent one or more values over time. With Observables, a developer can apply LINQ like operations to mutate observable event streams and accomplish their goals more elegantly. Achieve more and worry less, ReactiveUI and the reactive programming model are a perfect choice for most UIs.

Tip To quickly get started, visit the Installation page and install appropriate ReactiveUI packages. Platform-specific packages are required. This means your app won't perform as expected until you install the packages properly.

A portable and maintainable codebase is important, especially in large-scale and cross-platform .NET implementations. The Model-View-ViewModel pattern, MVVM for short, helps achieve maintainability goals in .NET powered platforms, such as Windows Presentation Foundation, Universal Windows Platform, Xamarin Forms, and Avalonia UI. With MVVM, the Model represents services, data transfer objects and database entities related to the application domain. The View is the user interface and exposes elements for user interactions. Finally, the ViewModel encapsulates the logic that facilitates communication between the Model and the View.

ReactiveUI allows you to combine the MVVM pattern with Reactive Programming using such features, as WhenAnyValue, ReactiveCommand, ObservableAsPropertyHelper, Binding and WhenActivated.

A Compelling Example

Let's create a simple application demonstrating a number of ReactiveUI functionalities, without getting into too many under-the-hood details. We will create a WPF application, which will allow us to search through NuGet public repositories. The full code of the application is shown at the end of this chapter, and we will show relevant snippets as we go.

1. Create the project

In Visual Studio, create a new WPF application (.NET 4.6.1 or above), use ReactiveDemo app name. Our view has been already created for us, the MainWindow, so we will proceed with creating our ViewModel.

2. Add NuGet packages

Install-Package ReactiveUI.WPF

The complete list containing NuGet packages for all supported platforms can be found here. ReactiveUI main package should normally be installed into you .NET Standard class libraries containing platform-agnostic code (repositories, services, DTOs, view-models), and ReactiveUI.XXX packages are platform-specific, so we use ReactiveUI.WPF in this tutorial as we are developing a tiny WPF application that doesn't need code sharing.

Install-Package NuGet.Client
Install-Package NuGet.Protocol.Core.v3

We also need a NuGet client library in this tutorial, and we are going to install and use NuGet Client.

3. Create ViewModels

// AppViewModel is where we will describe the interaction of our application.
// We can describe the entire application in one class since it's very small now. 
// Most ViewModels will derive off ReactiveObject, while most Model classes will 
// most derive off INotifyPropertyChanged
public class AppViewModel : ReactiveObject
    // In ReactiveUI, this is the syntax to declare a read-write property
    // that will notify Observers, as well as WPF, that a property has 
    // changed. If we declared this as a normal property, we couldn't tell 
    // when it has changed!
    private string _searchTerm;
    public string SearchTerm
        get => _searchTerm;
        set => this.RaiseAndSetIfChanged(ref _searchTerm, value);
    // Here's the interesting part: In ReactiveUI, we can take IObservables
    // and "pipe" them to a Property - whenever the Observable yields a new
    // value, we will notify ReactiveObject that the property has changed.
    // To do this, we have a class called ObservableAsPropertyHelper - this
    // class subscribes to an Observable and stores a copy of the latest value.
    // It also runs an action whenever the property changes, usually calling
    // ReactiveObject's RaisePropertyChanged.
    private readonly ObservableAsPropertyHelper<IEnumerable<NugetDetailsViewModel>> _searchResults;
    public IEnumerable<NugetDetailsViewModel> SearchResults => _searchResults.Value;

    // Here, we want to create a property to represent when the application 
    // is performing a search (i.e. when to show the "spinner" control that 
    // lets the user know that the app is busy). We also declare this property
    // to be the result of an Observable (i.e. its value is derived from 
    // some other property)
    private readonly ObservableAsPropertyHelper<bool> _isAvailable;
    public bool IsAvailable => _isAvailable.Value;

    public AppViewModel()
        // Creating our UI declaratively
        // The Properties in this ViewModel are related to each other in different 
        // ways - with other frameworks, it is difficult to describe each relation
        // succinctly; the code to implement "The UI spinner spins while the search 
        // is live" usually ends up spread out over several event handlers.
        // However, with ReactiveUI, we can describe how properties are related in a 
        // very organized clear way. Let's describe the workflow of what the user does 
        // in this application, in the order they do it.

        // We're going to take a Property and turn it into an Observable here - this
        // Observable will yield a value every time the Search term changes, which in
        // the XAML, is connected to the TextBox. 
        // We're going to use the Throttle operator to ignore changes that happen too 
        // quickly, since we don't want to issue a search for each key pressed! We 
        // then pull the Value of the change, then filter out changes that are identical, 
        // as well as strings that are empty.
        // We then do a SelectMany() which starts the task by converting Task<IEnumerable<T>> 
        // into IObservable<IEnumerable<T>>. If subsequent requests are made, the 
        // CancellationToken is called. We then ObservableOn the main thread, 
        // everything up until this point has been running on a separate thread due 
        // to the Throttle().
        // We then use a ObservableAsPropertyHelper and the ToProperty() method to allow
        // us to have the latest results that we can expose through the property to the View.
        _searchResults = this
            .WhenAnyValue(x => x.SearchTerm)
            .Select(term => term?.Trim())
            .Where(term => !string.IsNullOrWhiteSpace(term))
            .ToProperty(this, x => x.SearchResults);
        // We subscribe to the "ThrownExceptions" property of our OAPH, where ReactiveUI 
        // marshals any exceptions that are thrown in SearchNuGetPackages method. 
        // See the "Error Handling" section for more information about this.
        _searchResults.ThrownExceptions.Subscribe(error => { /* Handle errors here */ });

        // A helper method we can use for Visibility or Spinners to show if results are available.
        // We get the latest value of the SearchResults and make sure it's not null.
        _isAvailable = this
            .WhenAnyValue(x => x.SearchResults)
            .Select(searchResults => searchResults != null)
            .ToProperty(this, x => x.IsAvailable);
    // Here we search NuGet packages using the NuGet.Client library. Ideally, we should
    // extract such code into a separate service, say, INuGetSearchService, but let's 
    // try to avoid overcomplicating things at this time.
    private async Task<IEnumerable<NugetDetailsViewModel>> SearchNuGetPackages(
        string term, CancellationToken token)
        var providers = new List<Lazy<INuGetResourceProvider>>();
        providers.AddRange(Repository.Provider.GetCoreV3()); // Add v3 API support
        var packageSource = new PackageSource("");
        var source = new SourceRepository(packageSource, providers);

        var filter = new SearchFilter(false);
        var resource = await source.GetResourceAsync<PackageSearchResource>().ConfigureAwait(false);
        var metadata = await resource.SearchAsync(term, filter, 0, 10, null, token).ConfigureAwait(false);
        return metadata.Select(x => new NugetDetailsViewModel(x));

The goal of the ReactiveUI syntax for read-write properties is to notify Observers that a property has changed. Otherwise we would not be able to know when it was changed.

In cases when we don't need to provide for two-way binding between the View and the ViewModel, we can use one of many ReactiveUI Helpers, to notify Observers of a changing read-only value in the ViewModel. We use the ObservableAsPropertyHelper twice, once to turn a generic IEnumerable into an observable read-only collection, and then to change the visibility of an indicator to show that a request is currently executing.

This also works in the opposite direction, when we take the SearchTerm property and turn it into an observable. This means that we are notified every time a change occurs in the UI. Using Reactive Extensions, we then throttle those events, and ensure that the search occurs no sooner than 800ms after the last keystroke. And if at that point the user did not change the last value, or if the search term is blank, we ignore the event completely. We have another ObservableAsPropertyHelper property IsAvailable which is generated by determining if our current SearchResults is null.

Let's now create a NugetDetailsViewModel.cs class that will wrap out NuGet metadata into a more usable class for our View. It includes a ReactiveCommand for opening the NuGet Repository URL.

// This class wraps out NuGet model object into a ViewModel and allows
// us to have a ReactiveCommand to open the NuGet package URL.
public class NugetDetailsViewModel : ReactiveObject
    private readonly IPackageSearchMetadata _metadata;
    private readonly Uri _defaultUrl;

    public NugetDetailsViewModel(IPackageSearchMetadata metadata)
        _metadata = metadata;
        _defaultUrl = new Uri("");
        OpenPage = ReactiveCommand.Create(() => { Process.Start(ProjectUrl.ToString()); });
    public Uri IconUrl => _metadata.IconUrl ?? _defaultUrl;
    public string Description => _metadata.Description;
    public Uri ProjectUrl => _metadata.ProjectUrl;
    public string Title => _metadata.Title;

    // ReactiveCommand allows us to execute logic without exposing any of the 
    // implementation details with the View. The generic parameters are the 
    // input into the command and it's output. In our case we don't have any 
    // input or output so we use Unit which in Reactive speak means a void type.
    public ReactiveCommand<Unit, Unit> OpenPage { get; }

4. Create Views

ReactiveUI allows you to create views using two different approaches. The recommended approach is using type-safe ReactiveUI bindings that can save you from memory leaks and runtime errors. The second approach is using XAML markup bindings.

Create Views using ReactiveUI type-safe bindings (recommended)

First, we need to register our views in the App.xaml.cs file.

public partial class App : Application
    public App()
        // A helper method that will register all classes that derive off IViewFor 
        // into our dependency injection container. ReactiveUI uses Splat for it's 
        // dependency injection by default, but you can override this if you like.

Then we declare the XAML for our Main Window.

    Title="NuGet Browser" Height="450" Width="800"
    <Grid Margin="12">
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        <TextBlock FontSize="16" FontWeight="SemiBold" 
                   VerticalAlignment="Center" Text="Search for: "/>
        <TextBox Grid.Column="1" Margin="6 0 0 0" x:Name="searchTextBox" />
        <ListBox x:Name="searchResultsListBox" Grid.ColumnSpan="3" 
                 Grid.Row="1" Margin="0,6,0,0" HorizontalContentAlignment="Stretch"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled" />

We need to derive the MainWindow from IViewFor<T>, so we use ReactiveWindow<TViewModel> base class.

Note If there is no reactive base class for your view control that can suite you, simply implement the IViewFor<TViewModel> interface by hand. Remember to store the ViewModel in a DependencyProperty or in a BindableProperty. See Data Binding for platform-specific examples.

We are going to use ReactiveUI Binding to bind our ViewModel to our View. Reactive binding is a cross platform way of consistently binding properties on your ViewModel to controls on your View. The ReactiveUI binding has a few advantages over the XAML based binding. The first advantage is that property name changes will generate a compile error rather than runtime errors.

// MainWindow class derives off ReactiveWindow which implements the IViewFor<TViewModel>
// interface using a WPF DependencyProperty. We need this to use WhenActivated extension
// method that helps us handling View and ViewModel activation and deactivation.
public partial class MainWindow : ReactiveWindow<AppViewModel>
    public MainWindow()
        ViewModel = new AppViewModel();

        // We create our bindings here. These are the code behind bindings which allow 
        // type safety. The bindings will only become active when the Window is being shown.
        // We register our subscription in our disposableRegistration, this will cause 
        // the binding subscription to become inactive when the Window is closed.
        // The disposableRegistration is a CompositeDisposable which is a container of 
        // other Disposables. We use the DisposeWith() extension method which simply adds 
        // the subscription disposable to the CompositeDisposable.
        this.WhenActivated(disposableRegistration =>
            // Notice we don't have to provide a converter, on WPF a global converter is
            // registered which knows how to convert a boolean into visibility.
                viewModel => viewModel.IsAvailable, 
                view => view.searchResultsListBox.Visibility)
                viewModel => viewModel.SearchResults, 
                view => view.searchResultsListBox.ItemsSource)
                viewModel => viewModel.SearchTerm, 
                view => view.searchTextBox.Text)

Reactive Binding allows you to modify the ViewModel property by using Binding Converters. These Binding Converters are able to be registered globally or they can be declared locally. We are going to use Binding Converters in two instances, firstly we have a global converter registered by default on XAML projects that converts a boolean to Visibility, and the second to convert our project URL into a BitmapImage locally one.

We are now going to create a view for our NugetDetailsViewModel. Create a new UserControl named NugetDetailsView and change it's XAML to the following:

          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
      <Image x:Name="iconImage" Margin="6" Width="64" Height="64"
             HorizontalAlignment="Center" VerticalAlignment="Center"/>
      <TextBlock Grid.Column="1" TextWrapping="WrapWithOverflow" 
                 Margin="6" VerticalAlignment="Center">
          <Run FontSize="14" FontWeight="SemiBold" x:Name="titleRun"/>
          <LineBreak />
          <Run FontSize="12" x:Name="descriptionRun"/>
          <LineBreak />
          <Hyperlink x:Name="openButton">Open</Hyperlink>

Then edit code-behind of the NugetDetailsView.

// The class derives off ReactiveUserControl which contains the ViewModel property.
// In our MainWindow when we register the ListBox with the collection of 
// NugetDetailsViewModels if no ItemTemplate has been declared it will search for 
// a class derived off IViewFor<NugetDetailsViewModel> and show that for the item.
public partial class NugetDetailsView : ReactiveUserControl<NugetDetailsViewModel>
    public NugetDetailsView()
        this.WhenActivated(disposableRegistration =>
            // Our 4th parameter we convert from Url into a BitmapImage. 
            // This is an easy way of doing value conversion using ReactiveUI binding.
                viewModel => viewModel.IconUrl, 
                view => view.iconImage.Source, 
                url => url == null ? null : new BitmapImage(url))

                viewModel => viewModel.Title, 
                view => view.titleRun.Text)
                viewModel => viewModel.Description, 
                view => view.descriptionRun.Text)
                viewModel => viewModel.OpenPage, 
                view => view.openButton)

This view will automatically get displayed in the ListBox in the MainWindow. When using Reactive Binding on XAML platforms, if no ItemTemplate has been set, it will look for a IViewFor<T> inside our Dependency Injection and display the Item using that control. Notice we convert our URI above into a BitmapImage just for the OneWayBind. ReactiveUI allows us to quickly convert types which is much easier syntax than the XAML Value Converters.

Now you can search repositories on NuGet using your own app!

Create Views using traditional XAML markup bindings

If you would like to use XAML bindings (remember, they don't guarantee type-safety and don't provide tools for memory management, such as WhenActivated, but ReactiveUI bindings do), then this tutorial is for you.

The first thing you need to do is creating a converter, while we have a boolean property AppViewModel.IsAvailable indicating if our ViewModel has content loaded. Let's create a new class BoolToVisibilityConverter.cs.

// If we would like to do value conversion using Binding markup extension,
// we need to implement the IValueConverter interface.
public class BoolToVisibilityConverter : IValueConverter
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        // We need to convert True value to Visibility.Visible and False value to
        // Visibility.Collapsed. Then we need to declare the converter as a static resource.
        return (bool)value ? Visibility.Visible : Visibility.Collapsed;

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        // Our app doesn't need to be able to convert values back, so we won't implement this.
        return Binding.DoNothing;

Now we should initialize the DataContext of our MainWindow by assigning an instance of the AppViewModel to it. Go to MainWindow.xaml.cs and do this:

public partial class MainWindow : Window
    public MainWindow()
        DataContext = new AppViewModel();

Finally, we need to create XAML markup for our app.

<Window x:Class="ReactiveDemo.MainWindow"
        Title="NuGet Browser" mc:Ignorable="d" Height="450" Width="800">
            Here we declare the value converter that we've implemented 
            on the previous step. We will need it later to convert the
            IsAvailable boolean property to Visibility.
            <BooleanToVisibilityConverter x:Key="BoolToVisible" />
    <Grid Margin="12">
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        <TextBlock FontSize="16" 
                   Text="Search for: "/>
        Here we create a two-way binding for the SearchTerm property
        of our ViewModel. When a user types something into the TextBox,
        value of the SearchTerm property will be updated automatically.
        Don't forget to set UpdateSourceTrigger to PropertyChanged on
        WPF and UWP platforms.
        <TextBox Grid.Column="1" 
                 Margin="6 0 0 0"
                 Text="{Binding SearchTerm, 
        Here we bind the IsAvailable property to ListView's Visibility
        using the value converter we've declared above. Also, we bind
        SearchResults list to ListView's ItemsSource.
        <ListBox Grid.ColumnSpan="3" 
                 Grid.Row="1" Margin="0,6,0,0" 
                 ItemsSource="{Binding SearchResults}"
                 Visibility="{Binding IsAvailable, 
                                      Converter={StaticResource BoolToVisible}}">
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                        <Image Margin="6" Width="64" Height="64" 
                               Source="{Binding IconUrl, Mode=OneWay}"
                        <TextBlock Grid.Column="1" Margin="6"
                            <Run FontSize="14" FontWeight="SemiBold" 
                                 Text="{Binding Title, Mode=OneWay}"/>
                            <LineBreak />
                            <Run FontSize="12" Text="{Binding Description, Mode=OneWay}"/>
                            <LineBreak />
                            <Hyperlink Command="{Binding OpenPage}">Open</Hyperlink>

Now you can search repositories on NuGet using your own app!

Source code of the application described in this guide can be found on GitHub.

Explore ReactiveUI

Now you know ReactiveUI, but we have more to offer. If you'd like to discover all features ReactiveUI has, visit our Handbook! Take a look at a truly cross-platform demo app that works on each platform ReactiveUI supports.

Also give a try to DynamicData - reactive collections based on reactive extensions. Use DynamicData for transforming and observing dynamically changing data sets in your ReactiveUI applications. You can also watch videos about Reactive Extensions and ReactiveUI, or view sources of open-source applications built with ReactiveUI. We have a blog and a twitter account for you to stay tuned. Star ReactiveUI on Github!