ReactiveUI (RxUI)

 
14 april 2014

Wat is RxUI?

In het woud van MVVM frameworks is er een nieuwe speler, of eigenlijk niet zo’n hele nieuwe speler, maar na de post van Arno over Reactive Extensions, is ReactiveUI op de voorgrond verschenen. Mocht je die post (http://www.software-innovators.nl/2014/01/10/reactive-extensions-rx/ ) nog niet hebben gelezen, het is een aanrader om dat eens te bekijken want ReactiveUI is een MVVM framework gebaseerd op het Reactive Extensions paradigma. Zoals ieder ander MVVM framework heeft ook RxUI een aantal bekende onderdelen, die op een eigen manier zijn geimplementeerd.

  • Een klasse die geimplementeerd dient te worden door de viewmodels (ReactiveObject), waarna er gebruik gemaakt kan worden van de INotifyPropertyChanged interface (dit zorgt ervoor dat de UI word geinformeerd over veranderingen in het viewmodel).
  • Commands (ReactiveCommand) die gebruikt kunnen worden om UI acties af te vangen en om te zetten in een bepaalde acties in het viewmodel (denk aan in of uitloggen of navigeren)
  • Een messagebus. Hiermee kan er gecommuniceerd worden tussen de verschillende viewmodels en views. Pas hiermee wel op, en zorg dat je geen memoryleaks creëert.

Dit zijn de meest voorkomende onderdelen bij eigenlijk ieder MVVM framework, ook bij RxUI zal je deze onderdelen het meest tegenkomen en gebruiken. Daarnaast heeft RxUI nog een onderdeel wat niet elk MVVM framework heeft, maar wat RxUI zo sterk maakt:

  • De bindingengine, leeft aan de view kant, en zorgt ervoor dat de gemaakte viewmodels gekoppeld kunnen worden aan de view.

In de korte tijd dat ik er mee heb kunnen werken, ben ik er wel fan van geworden, vooral de goede scheiding tussen design en logica spreekt mij aan. Helaas is er qua documentatie nogal veel verouderde data te vinden op internet. De samples en docs kunnen met gemak 2 jaar oud zijn, met weinig recente voorbeelden . Pauls Betts de bedenker van RxUI is daarentegen wel erg actief op fora als StackOverflow om vragen te beantwoorden.

Zelf heb ik een aantal speeltuin applicaties gemaakt, en ik wil jullie daarom graag eens wijzen op het bestaan van RxUI en een aantal voordelen er van aanduiden. Mijn voorbeeld project is hier te downloaden: Stopwatch project

WPF <-> Winforms: IViewFor<>

Wat RxUI zo sterk maakt is dat het voor de meeste gangbare platformen gebruikt kan worden. Zo word op de site zelf genoemd WPF, Silverlight, WinRT, and Windows Phone 7, maar ook Android & Iphone apps zouden er mee ontwikkeld kunnen worden. Zelf heb ik alleen nog maar WPF en Winforms kunnen testen, maar de resultaten waren erg positief.

Omdat RxUI een MVVM framework is zul je er altijd voor proberen te zorgen dat de meeste logica in je viewmodels zit. Hierdoor is het sowieso al erg goed herbruikbaar. Maar met RxUI heb je ook nog de beschikking over een bindingengine, dit zorgt ervoor dat je zelfs de bindings maar eenmalig hoeft te maken. Dit doe je doormiddel van de IViewFor<> interface te implementeren in je views. Hierdoor is de view bekend met het viewmodel en kun je in je constructor aangeven wat waarop moet binden.

Bijvoorbeeld:

public partial class MainWindow : IViewFor<MainWindowViewModel>
{
public MainWindow()
{
InitializeComponent();

//bij gebrek aan een goede bootstrapper
ViewModel = new MainWindowViewModel();

this.Bind(ViewModel, vm => vm.Title);
}

object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (MainWindowViewModel)value; }
}

public MainWindowViewModel ViewModel { get; set; }
}

Hier word beschreven, pak van het MainWindowViewModel de property Title en bind hem op een control met de naam Title. De bindingengine zal de default property van het control gebruiken (in dit geval Text). Als je iets explicieter wilt zijn, en bij sommige properties zal dat moeten (denk aan lijsten of visibility) dan kun je dat zo beschrijven:

this.Bind(ViewModel, vm => vm.Title, v => v.Title.Text);

Dit beschrijft precies hetzelfde als het eerste stukje code, je bent hier alleen een stuk explicieter. RxUI beschikt over een aantal default converters zodat je nooit meer hoeft te rommelen met bijvoorbeeld de befaamde “BooleanToVisiblityConverter”.

Overigens is deze bindingengine geen vervanging van de bestaande, maar meer een aanvulling op. De bestaande bindingengines (WPF of Winforms) kunnen naast RxUI ook nog gebruikt worden. Maar waarom zou je dat doen, als dit het je zo makkelijk maakt?

Deze manier van binden zorgt ervoor dat je in elk platform op dezelfde manier bind. Je hoeft dan alleen nog maar te zorgen dat er ook daadwerkelijk controls bestaan met de aangegeven naam in de designer. In mijn test heb ik voor WPF en Winforms exact dezelfde codebehind en viewmodels, het enige dat verschil is de designer. En is dat nou niet juist waar iedere ontwikkelaar van droomt, een duidelijke scheiding tussen design en logica?

WhenAny

Wat ik zelf wel handig vond, is de WhenAny methode. Waarmee je je eigenlijk aboneert op changes van een bepaalde property, om daar vervolgens een actie op uit te kunnen voeren. Allemaal in de wellicht bekende Rx chain. Niet meer events hoeven aanmaken om parents op de hoogte te brengen van wat z’n child doet, geen gerommel in setters van andere properties, gewoon WhenAny en Subscribe.

var runningTask = new RunningTask();
runningTask.WhenAny(vm => vm.ElapsedTime, vm => vm.Value).Subscribe(x =>
{
var total = TimeSpan.FromMilliseconds(0);
TotalElapsed = RunningTasks.Aggregate(total, (current, existingTask) => current.Add(existingTask.Time)).ToString("g");
});
RunningTasks.Add(runningTask);

Hier check ik of de property ElapsedTime van de runningtask veranderd. Zo ja, dan bereken ik hoeveel tijd er in totaal verstreken is van alle runningtasks in de collectie, en zet ik daarmee het TotalElapsed veld. Het TotalElapsed veld is dan weer gebind aan een TextBlock, die dus iedere keer dat er een runningtask veranderd zal updated naar de juiste verstreken tijd.

ReactiveCommand

Wat je waarschijnlijk ook veel gaat tegenkomen met RxUI en eigenlijk MVVM in het algemeen, is het gebruik van commands. Bij RxUI is deze bekend als “ReactiveCommand”.  Wat erg makkeiljk aan dit ReactiveCommand is, is dat bij het instantieren een observable(Rx) meegegeven kan worden. Deze observable zorgt ervoor dat een bepaald command afgevuurd kan worden of niet. Dit kan erg handig zijn als je bijvoorbeeld de login button van je applicatie wil disablen als er geen gebruikersnaam en wachtwoord zijn ingevuld. Dit ziet er dan zo uit:

LoginCommand = new ReactiveCommand(this.WhenAny(x => x.UserName, x => x.Password, (userName, password) =>
!string.IsNullOrWhiteSpace(userName.Value) &&
!string.IsNullOrWhiteSpace(password.Value)));

Hier word een ReactiveCommand aangemaakt die ervoor moet zorgen dat er ingelogd kan worden met dit viewmodel. Als observable geven we een WhenAny mee, daarin geven we aan dat pas wanneer de userName en password gevuld zijn, de command uitgevoerd kan worden. De LoginCommand is gewoon een property in het viewmodel waarop gebind kan worden in de constructor (of andere plek waar jij het nodig hebt) van een view. Dit doe je op de volgende manier:

this.BindCommand(ViewModel, vm => vm.LoginCommand, v => v.LoginButton);

RxUI bind weer automatisch op de juiste property van het control, in dit geval de Command property van onze login button. Maar we kunnen ook de command af laten vuren als we een mouseenter event krijgen. Dat ziet er ongeveer zou uit:

this.Events().MouseEnter.InvokeCommand(ViewModel.StartNewRunningTask);

De events collectie zorgt ervoor dat je alle events van controls op de Rx manier kan gebruiken.

Bij een command hoort natuurlijk ook een stuk code dat aangeroepen moeten worden als de command word uitgevoerd. Dit doe je door middel van .Subscribe(). De subsribe kan overal plaatsvinden waar je wil, dit kan in het viewmodel zijn maar ook in een view. Bijvoorbeeld in het viewmodel wanneer er uitgelogd word:

ConfirmLogoffCommand = new ReactiveCommand();

ConfirmLogoffCommand.Subscribe(_ => {

LoggedInUser = string.Empty;

//Doe andere uitlog dingen

});

Of in de view wanneer er uitgelogd word:

ViewModel.LogOffCommand.Subscribe(x => {
var result = MessageBox.Show(“Weet je het zeker?”, “Bevestigen”, MessageBoxButton.YesNo)

//Doe andere dingen
//Vuur bijvoorbeeld bij yes, het ConfirmLogoffCommand af
//ViewModel.ConfirmLogoffCommand.Execute(null);

});

Experimenteer

Er zijn nog zoveel andere onderwerpen die ik tegengekomen ben bij RxUI (Routing, MessageBus, UserError, Scheduling), maar vanwege de lengte van de post zal ik daar nu nog niet op in gaan, wie weet komt er een follow up. De 3 bovenstaande onderwerpen zal in elk geval iedereen gaan tegenkomen bij het gebruik van RxUI.

In de korte tijd dat ik naar RxUI heb gegkeken, heb ik maar de basis gezien van wat mogelijk is, daarom kan ik je aanraden om er eens mee te experimenteren, en te zien wat jij er allemaal mee kan.

Wil je meer weten over RxUI?

Kijk dan eens rond op de volgende paginas:

http://www.software-innovators.nl/2014/01/10/reactive-extensions-rx/ de post van Arno over Rx
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx Microsoft over MVVM
http://www.reactiveui.net/ officiele site van RxUI
http://blog.paulbetts.org/ of http://log.paulbetts.org/ de sites van de bedenker van RxUI
https://github.com/reactiveui/ReactiveUI/tree/docs/docs/basics docs over RxUI, verouderd maar bruikbaar
https://github.com/reactiveui/ReactiveUI.Samples voorbeelden van gebruik RxUI, verouderd maar bruikbaar

Of download mijn speeltuin stopwatch project, waar ik een aantal elementen van wat ik net heb verteld toepas:
Stopwatch project


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development