Reactive MVVM Pattern On The .NET Platform
A portable and maintainable codebase is important, especially in large-scale and cross-platform .NET implementations. On XAML-based platforms, such as Windows Presentation Foundation (WPF), Universal Windows Platform (UWP), Xamarin Forms, and AvaloniaUI you can achieve maintainability goals by implementing the MVVM pattern.
MVVM stands for Model-View-ViewModel, where Model represents services, data transfer objects and database entities related to the application domain, View is the UI and ViewModel’s responsibility is to tie these two layers together in a convenient way. ViewModel encapsulates interaction with Model, exposing properties and commands for XAML UI to bind to.
Traditional MVVM Implementation
To make bindings work a typical ViewModel should implement the INotifyPropertyChanged interface and call the PropertyChanged event when any of ViewModel’s properties change. A straightforward implementation may look like this:
With the following XAML describing our UI:
Works like a charm. When a user types their name into the text box, the text block placed below displays: “Hello, %username%!”. Values of these two controls stay synchronized — when text box’s content changes, text block updates itself immediately. Button clears the text box.
But wait! Our UI only needs two synchronized observable properties and one command, why do we have to write more than 20 lines of code to achieve that? What will happen, if we decide to add more properties representing ViewModel state? The code will bloat, becoming increasingly harder to understand and maintain!
Recipe #1. Observables. Shorter Getters And Setters. ReactiveUI
Well, the property change notifications boilerplate code issue is not new, and there are several solutions. Let’s take a look at ReactiveUI. It is a cross-platform, composable, functional reactive MVVM framework that brings the power of Reactive Extensions for the .NET platform. It also provides an INotifyPropertyChanged base class named ReactiveObject and a bunch of extension methods. Let’s modify our code snippet and see, how it’s reactive version looks like.
This snippet does exactly the same as previous one but is shorter, more predictable, easier to understand and maintain. Property relations are described in one place using declarative ReactiveUI syntax. But this code is still quite verbose — we have to implement getters, setters and backing fields explicitly.
Recipe #2. Boilerplate Code Encapsulation. Reactive Property
Another solution is to use reactive bindings from ReactiveProperty library. This package provides wrapper classes responsible for sending notifications to the UI. Here ViewModel does not need to implement any interfaces, instead, each property implements INotifyPropertyChanged itself. Such properties also are observables — they can be mapped, filtered, combined and so on. Let’s rewrite our sample using ReactiveProperty.
We only need to declare and initialize reactive properties and describe relations among them. No boilerplate code needed, except for property initializers. But this approach has a drawback — we should modify our XAML markup to make these bindings work as expected. Reactive properties themselves are wrappers, so UI needs to bind to each wrapper’s own property.
Recipe #3. Assembly Weaving. Fody + ReactiveUI
In a typical ViewModel, each property should send notifications to the UI when it’s value changes. With PropertyChanged.Fody package, a developer doesn’t have to take care of this. The only requirement is a ViewModel being marked with AddINotifyPropertyChangedInterface attribute — and code raising the property change event will be injected into setters automatically during the project build. If we need to turn our properties into observables, we can always use the WhenAnyValue extension method from ReactiveUI. Let’s rewrite our snippet again and see, how concise our code will become!
Fody manipulates the IL of an assembly at build time. PropertyChanged.Fody add-in searches for all classes implementing the INotifyPropertyChanged interface or marked with AddINotifyPropertyChangedInterface attribute, and modifies those classes’ property setters.
While this approach is great and allows us to write clean and simple code, outdated .NET Framework versions, including 4.5.1 and older ones, are no longer supported. This means you can actually use Fody with outdated versions but at your own risk while keeping in mind that any bugs found won’t ever get fixed. .NET Core versions are supported according to .NET Core support policy.
Recipe #4. Assembly Weaving. ReactiveUI.Fody
Yet another option is to use ReactiveUI.Fody package, which is now fully compatible with .NET Core. It works similarly to PropertyChanged.Fody, but uses opt-in approach — you need to mark all properties with ReactiveUI.Fody attributes explicitly to have INotifyPropertyChanged boilerplate code injected into them at compile time. Additionally, this approach allows you to create get-only properties based on ObservableAsPropertyHelper that will always contain the latest value from an observable stream.