Hello friends, today we will have an introduction to my favorite MVVM framework ReactiveUI. I wrote an article about reactiveui a while ago you can find it here. But that article is outdated, since the framework has evolved. The software development world grows so fast and this article can be seen as my effort to keep up the pace.
What is Reactiveui
As mentioned on their website, Reactiveui is An advanced, composable, functional reactive model-view-viewmodel framework for all .NET platforms!
Reactiveui leverages reactive programming to facilitate the implementation of the MVVM pattern in your apps. Also, Reactiveui is not very opinionated as compared to other MVVM frameworks. Ever heard of Reactive Extensions ?Reactive Extensions (RX) is a library which provides you a convenient way to declare callbacks and manage asynchronous executions in your code and it does all of this using the LinQ syntax. It makes your source code more readable and avoids ? spaghetti code. Now Imagine you could leverage all of these to implement the MVVM architectural design pattern. This is what ReactiveUI permits developers to do and a lot more.
What we will build
Reactiveui is available on several platforms but in this post, we will use reactiveui in a Xamarin Forms application. Its usage is similar on every platform. So knowing how to use reactiveui with Xamarin Forms will still be profitable to you even if you are a WPF developer.
We will build a simple TODO application which will showcase several features of reactiveui.
If you like this post, don’t hesitate to follow me on Twitter or Github and subscribe to this page’s notifications to stay updated with new articles or like my page on Facebook.
Here is the source code for the app implemented in this blog post.
Model, ViewModel and the ReactiveObject
The reactive object contains an implementation of INPC. Thus it is used to notify property changes on view models and if necessary on models too. To notify property changes in a property, this method is used:
1 | this.RaiseAndSetIfChanged(ref _itemTitle, value); |
For example, in our todo app, the base view model has a property named “IsBusy”. The role of this property is to notify the UI that the app is busy everytime its value is set to true.
1 2 3 4 5 6 7 8 9 10 11 12 | public class BaseViewModel : ReactiveObject, IValidatableViewModel { bool isBusy = false; public bool IsBusy { get { return isBusy; } set { this.RaiseAndSetIfChanged(ref isBusy, value); } } // Code omitted for simplicity... } |
In our case, the model is represented by the Todo class. This class also inherits from the ReactiveObject we will discuss why later.
Commands
In reactiveui, commands are made using the ReactiveCommand. It permits us to create commands from simple methods, from tasks, from observables… Here are a few examples bellow.
Bellow, we want to perform a simple synchronous operation in our command so we create it using a normal method.
1 2 3 4 5 6 7 8 9 | public ICommand CancelCommand { get; private set; } ... CancelCommand = ReactiveCommand.Create(OnCancelCommand); ... public void OnCancelCommand() { MessagingCenter.Send(this, "PopModal"); } |
Bellow, we want to perform an asynchronous operation in our command. So, we create our command from a method which returns a task. At the same time the command should have a parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public ICommand DeleteCommand { get; private set; } ... DeleteCommand = ReactiveCommand.CreateFromTask<Item>(ExecuteDeleteItem, NotBusyObservable); ... async Task ExecuteDeleteItem(Item item) { //Call an interaction to confirm item deletion var result = await DeleteItemInteraction.Handle(this); if (result) { IsBusy = true; await Task.Delay(TimeSpan.FromSeconds(2)); Items.Remove(item); } IsBusy = false; } |
Observing Property changes
This is one of my favorite feature in reactiveui. You can easily observe property changes in your view model and perform actions such as Changing a command’s execution state. Some other implementations of MVVM require developers to go in the property’s setter to perform actions when a value changes. You can even combine property changes to create a unique observable.
For example, in our TODO application, we want some commands to be unable to execute when the view model is busy. With reactiveui, we the “WhenAnyValue()” method to create an observable, and pass that observable as parameter when creating our commands. That is all.
1 2 3 4 5 6 7 8 9 10 11 | /// <summary> /// Observes the busy state of the viewmodel /// Is used to activate or not a command depending on wether /// The view model is busy or not /// </summary> protected IObservable<bool> NotBusyObservable { get; private set; } ... NotBusyObservable = this.WhenAnyValue(vm => vm.IsBusy, isbusy => !isbusy); ... //You delete items only if the viewmodel is NOT busy DeleteCommand = ReactiveCommand.CreateFromTask<Item>(ExecuteDeleteItem, NotBusyObservable); |
NB: in case property changes are not fired in your observable, you will have to install an additional nuget package. “Fody” and add the “[Reactive]” attribute to the property you want to observe.
For example, observing several properties at a time. Below, we want to enable the save command only when a todo item’s title and description has been set.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | private ValidatableObject _itemTitle; [Reactive] public ValidatableObject ItemTitle { get { return _itemTitle; } set { this.RaiseAndSetIfChanged(ref _itemTitle, value); } } private ValidatableObject _itemDescription; [Reactive] public ValidatableObject ItemDescription { get { return _itemDescription; } set { this.RaiseAndSetIfChanged(ref _itemDescription, value); } } ... //Create an observable stating if we can save this item or not. var canSaveObservable = this.WhenAnyValue(vm => vm.ItemDescription.Value, vm => vm.ItemTitle.Value, (desc, title) => { return ItemDescription.TryValidate(desc) && ItemTitle.TryValidate(title); }); ... SaveCommand = ReactiveCommand.Create(OnSave, canSaveObservable); |
Dynamic Data
Have you ever wanted a tool which will permits you to leverage the power of reactive extensions in a collection of items ? Dynamic data is made for you. In this article we will only have a little introduction to Dynamic data. You can find more about it here.
Dynamic data is kind of an evolution of ReactiveLists which is now deprecated. Dynamic data is found in a different nuget package
In our list of todo items, we want an item to be removed from the list if it is marked as completed. To do this, we need to monitor property changes for items in our collection. Dynamic data permits us to do this. This is why our Todo class inherits from the reactive object. To notify when its “IsCompleted” property is set to true.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //We have a source list for our dynamic data SourceList<Item> _items; ... //Once the Items Observable collection is filled, we convert it to an observable and connect it to our source list. var items = await DataStore.GetItemsAsync(true); foreach (var item in items) { Items.Add(item); } _items = new SourceList<Item>(Items.ToObservable().ToObservableChangeSet()); //Observe when a list's item property changed //Add the autorefresh to observe everytime the property changes value var completedPropertyChangeObservable = _items.Connect().AutoRefresh(t => t.Completed).WhenPropertyChanged(todo => todo.Completed); completedPropertyChangeObservable.Subscribe(async observer => { var item = observer.Sender; var completed = observer.Value; if (completed) { IsBusy = true; await Task.Delay(TimeSpan.FromSeconds(2)); Items.Remove(item); IsBusy = false; } }); |
Validation
The ability to validate user input is a crucial part of your view model. Reactiveui permits you to validate data on the fly using its built in methods “WhenAny and WhenAnyValue”. You can use these methods to create observables and check data validity.
But for more complex scenarios, the above method of validation might not be ideal. You can use Reactiveui.Validation which provides a way to perform validation in a more sophisticated way. You can find more about it here.
Though these are great ways to implement validation in Reactiveui, there is another way. Based on David Britch’s article about enterprise validation. I implemented his approach but added Reactiveui’s features on top to provide a better data validation tool.
First we have the ValidatableObject, which represents the data object to be validated. It leverages reactiveui’s observable to perform automatic validation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public ValidatableObject() { Validations = new List<IValidationRule<T>>(); Errors = new List<string>(); this.WhenAny(obj => obj.Value, value => value).Subscribe((value) => { Validate(); }); } /// <summary> /// Tells if the object is valid without /// setting its state /// </summary> /// <returns></returns> public bool TryValidate() { return TryValidate(Value); } public bool TryValidate(T val) { Errors.Clear(); Errors = new List<string>(Validations.Where(v => !v.Check(val)) .Select(v => v.ValidationMessage)); return !Errors.Any(); } public bool Validate() { IsValid = TryValidate(); if (Errors.Any()) { ErrorMessage = string.Join(Environment.NewLine, Errors); } else { ErrorMessage = string.Empty; } return IsValid; } |
Then you have the validation rules which are added to your validation object. The validation object has a set of rules you give it, then observes when its value change to check its value’s validity. This is absolutely simple and clean validation.
The complete implementation is found in this repository. This is how to use it in your view model:
1 2 | ItemTitle = new ValidatableObject<string>(); ItemTitle.Validations.Add(new IsNotNullOrEmptyRule("Title should not be empty")); |
Service Location with Splat
Splat is a light weight library which has several functionalities. It can be used as a dependency injection container. In this situation, you can think of it as the dependency injection container found in your View Model Locator or App Locator. Bellow we have an example:
1 2 3 4 5 6 7 8 9 10 11 12 | public class AppLocator { public static NewItemsViewModel NewItemsViewModel => Locator.Current.GetService<NewItemsViewModel>(); public static ItemsViewModel ItemsViewModel => Locator.Current.GetService<ItemsViewModel>(); static public void Init() { Locator.CurrentMutable.Register(() => new ItemsViewModel()); Locator.CurrentMutable.Register(() => new NewItemsViewModel()); ... |
Splat is found in a different nugget package. You can learn more about splat here and all its features.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //An example of a rule: public class IsNotNullOrEmptyRule : BaseRule<string> { public IsNotNullOrEmptyRule(string message) : base(message) {} public override bool Check(string value) { if (value == null) { return false; } var str = value as string; return !string.IsNullOrWhiteSpace(str); } } |
Interactions
Interactions in reactiveui is a tool used to communicate between your app’s components. It reminds me of the Messaging Center in Xamarin Forms. You can use it in scenarios like requesting user confirmation before performing specific actions in your view model. In our app, we will use interactions to call from the view model a confirmation dialog in the view before deleting a todo item.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public Interaction<ItemsViewModel, bool> DeleteItemInteraction => _deleteItem; ... async Task ExecuteDeleteItem(Item item) { //Call an interaction to confirm item deletion var result = await DeleteItemInteraction.Handle(this); if (result) { IsBusy = true; await Task.Delay(TimeSpan.FromSeconds(2)); Items.Remove(item); } IsBusy = false; } |
Meanwhile in the view:
1 2 3 4 5 | viewModel.DeleteItemInteraction.RegisterHandler(async interaction => { var result = await DisplayAlert("Delete", "You are about to delete this item do you want to proceed ?", "Yes", "No"); interaction.SetOutput(result); }); |
Conclusion
Using our simple Xamarin Forms todo application, we explored several features of Reactiveui. There you have an overview of using Reactiveui with Xamarin Forms. Though Reactiveui is cross-platform, and what is demonstrated here works on other platforms.
References
https://github.com/reactiveui/ReactiveUI.Validation
https://devblogs.microsoft.com/xamarin/validation-xamarin-forms-enterprise-apps/
Follow me on social media and stay updated
Anthony Armfield
Damien Doumer