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.
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.
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.
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.
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
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.
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.
Finally, we get a good-looking feedback form component!
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.
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.
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.
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: https://github.com/worldbeater/ReactiveMvvm 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.