Building Cross-Platform Reactive .NET Apps

Artyom Gorchakov
Feb 24, 2018 · 5 min read

In the previous article, we took a look at several ways of implementing the MVVM pattern on the .NET platform. We found out, that the simplest approach is to use assembly weaving with reactive bindings and extensions. Now we are going to build a sample application using these techniques. Let’s start with the most common task — form validation.

Getting Started

The MVVM (Model-View-ViewModel) architectural pattern enforces a separation between three software layers, so we can think of extracting them into different assemblies. At this point we might want to take a look at .NET Standard — a formal specification of .NET APIs that are available across all .NET implementations.

MVVM code sharing strategy

Using the MVVM architectural approach and .NET Standard code sharing strategy, we can share our ViewModel and Model layers across multiple XAML UI frameworks. If we’ve built an app for Universal Windows Platform, it would be rather easy to port it to other UI frameworks, such as Xamarin.Forms, Avalonia or Windows Presentation Foundation — UI will be the only thing we’ll need to build from scratch.

Cross-platform .NET application architecture diagram created using ReSharper Architecture View tool

ReactiveUI and PropertyChanged.Fody libraries are fully compatible with .NET Standard, so we’ll use them in our sample application.

Form Validation Logic

Now we are ready to create our first reactive view model and populate it with some logic. Imagine we are developing a complex system and would like to gather feedback from users.

When a user sends us a message, we need to know whether the message is a bug report or a feature suggestion, we also need to group messages by categories. Users shouldn’t be able to submit feedback until they provide all necessary information. A view model satisfying these conditions might look like the one below.

Feedback Form view model built using Reactive UI and PropertyChanged.Fody

We mark our view model class with AddINotifyPropertyChangedInterface attribute — so all the declared properties will notify our UI when their values change. Using the WhenAnyValue extension method, we can subscribe to such notifications. A call to WhenAnyValue returns IObservable<T>, where T represents the type of the observed property. Then, we apply the Where operator from reactive extensions which allows us to filter irrelevant events. Inside a lambda passed to the Subscribe method we perform another view model property mutation.

WhenAnyValue allows us to observe as many properties as we would like to, so we validate the whole form each time any of the form fields change. The Log extension method is a magic utility that logs all values emitted by an observable to the debug output. There are also logging adapters for NLog, Serilog and Log4Net. The Publish and RefCount operators are used to ensure the observable event stream will be subscribed only once. We pass the IObservable<bool> to the ReactiveCommand that is responsible for form submitting, so it will stay disabled until a user fills the form up.

We mark the view model class with IEnableLogger and ISupportsActivation interfaces. The former is used to enable logging extensions, and the latter — to enable the WhenActivated feature. The DisposeWith extension method attaches the disposable produced by Subscribe to the CompositeDisposable which will be disposed when the view model gets deactivated.

Unit Testing

Testing is an important part of software development process. We can use XUnit to run unit tests either on .NET Core or on .NET Framework. NSubstitute library allows us to easily generate stubs and mocks, and FluentAssertions can improve readability of our tests’ stack traces. Let’s write a test to ensure our presentation logic performs as expected!

UI For Universal Windows Platform

Creating presentation part of the application is simple: we need to declare controls in XAML, and bind their values to view model’s properties and commands. Let’s see how:

We need to implement the IViewFor<TViewModel> interface in our platform specific view class. This makes activation and deactivation feature available for both our view and the associated view model. The view model should implement the ISupportsActivation interface. A call to WhenActivated added to the view constructor will also activate the view model.

IViewFor<TViewModel> implementation for a default Universal Windows Page

Finally, we get a good-looking feedback form component!

Feedback Form component built for Universal Windows Platform using Reactive UI and Fody

UI For Xamarin Forms

To port the app to Android devices, we need to create a new Xamarin.Forms project from Visual Studio templates and add a reference to our .NET Standard class library containing view models. XAML markup will look like the one created for Universal Windows Platform, but we’ll use cross-platform mobile-friendly controls provided by Xamarin.Forms. We also need to install ReactiveUI.AndroidSupport package into the Xamarin.Android project.

Feedback Form component built for Xamarin.Forms using Reactive UI and Fody

UI For Avalonia & Windows Presentation Foundation

It’s really easy now to port our application to Avalonia and Windows Presentation Foundation frameworks. For each framework we should create a platform-specific project and reference our .NET Standard class library. Also, we need to install ReactiveUI platform-specific packages according to ReactiveUI installation guide. From that point we can start building the UI.

Feedback form component built for Avalonia UI using Reactive UI and Fody

UI For Windows Forms

Another useful feature ReactiveUI provides is type-safe binding that can be used on any platform, even if it isn’t XAML-based. For example, with ReactiveUI.WinForms package we can use the MVVM pattern with Windows Forms. All we need to do is to create a new form, implement the IViewFor interface and place binding extension methods into the WhenActivated block.

Feedback form component built for Windows Forms using Reactive UI and Fody

As we see, .NET in 2018 allows us to build truly cross-platform software — using UWP, Xamarin.Forms, WPF and Avalonia UI XAML frameworks we can bring our applications to devices running Android, iOS, Windows, Linux and Mac OSX operating systems. MVVM pattern and such libraries, as ReactiveUI and Fody, can simplify the process of creating portable software, allowing developers to write clear and maintainable code.

Source code of the application described in this article can be found on GitHub: Additionally, you may take a look at a cross-platform application that demonstrates the usage of all the features described above, compatible with Windows, Linux, macOS and Android.

Artyom Gorchakov

Written by

Full stack .NET developer, open-source enthusiast. In love with reactive programming & clean code.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade