MVVM met Catel

Catel is een relatief nieuwe MVVM toolkit dat probeert MVVM zo eenvoudig mogelijk te maken zonder daarmee functionaliteit in te leveren. Catel is ontwikkeld omdat de makers op zoek waren naar een MVVM framework dat voldeed aan veel eisen die in grotere applicaties van belang zijn. Zo vonden zij het onnodig om steeds handmatig het PropertyChanged event te moeten aanroepen wanneer ze een property waarde zetten.

Een ander groot probleem in MVVM is nested user controls, ofwel hierarchie in view models. Het probleem is dat geen enkel framework een oplossing biedt voor dit probleem. De enige mogelijkheden die een programmeur heeft zijn ofwel in een view model een lijst van view models maken en deze binden ofwel een top view model dat ook implementatie verzorgd voor onderliggende user controls. Beide opties waren voor de ontwikkelaars van Catel geen optie, vandaar de behoefte om een eigen framework te schrijven en te delen met de rest van de wereld.
 
Dit artikel gaat niet in op de basis van MVVM, daar is immers genoeg over geschreven. Mocht MVVM een nieuw begrip zijn, dan wordt aangeraden om eerst de basisbegrippen eigen te maken.
Basis van Catel
De basis van Catel is bijzonder eenvoudig. In Catel zit, net zoals in ieder ander framework, een belangrijke class genaamd ViewModelBase. Dit is de base class voor alle view models. Het verschil met andere frameworks is echter dat de ViewModelBase niet alleen de INotifyPropertyChanged implementeerd, maar ook IDataErrorInfo en vele andere interfaces.
 
Door gebruik te maken van een “dependency properties”-a-like property registratie kan veel plumbing uit handen worden genomen die een developer anders zelf voor zijn rekening moet nemen. Voorbeelden hiervan zijn het aanroepen van het PropertyChanged event en het (opnieuw) aanroepen van validatie. Onderstaande code-voorbeeld geeft aan hoe eenvoudig het is om een property te registreren in een view model:
 
public string Name
{
    get { return GetValue<string>(NameProperty); }
    set { SetValue(NameProperty, value); }
}
 
public static readonly PropertyData NameProperty =
    RegisterProperty("Name", typeof(string));
 
Listing 1: Registreren van een property in een view model
 
Zoals te zien in listing 1 is het bijzonder eenvoudig om een property te declareren. Omdat het registreren van properties soms als boiler plate kan worden beschouwd biedt Catel ook code snippets die het registreren van properties veel eenvoudiger (en foutloos) maken.
 
Catel werkt met eenvoudige property registratie en services voor externe functionaliteit
 
 
Een ander voordeel van Catel is dat het werkt met services. Een view model kan gebruik maken van externe services die door middel van een IoC container bepaald kunnen worden. Een voorbeeld hoe men een message box toont is te vinden in listing 2.
 
var messageService = GetService<IMessageService>();
messageService.ShowError("An error occurred");
 
Listing 2: Gebruik van services in een view model
 
Catel levert standaard veel services, zoals een IUIVisualizerService (tonen van UI dialogs), IProcessService (starten van processen), INavigationService (navigeren tussen view models), en meer.
Waarom Catel
Veel ontwikkelaars zullen zich afvragen waarom er gekozen is om (weer) een nieuw MVVM framework te ontwikkelen. Als we strict kijken zijn er maar enkele serieuze MVVM frameworks. De meest bekende zijn MVVM Light en Caliburn (Micro).
 
MVVM Light implementeert slechts de basis van MVVM en mist veel “convenience” functionaliteit. Zo is het bijvoorbeeld noodzaak om altijd zelf een PropertyChanged aan te roepen na het zetten van de waarde van een property. Caliburn (Micro) is een framework dat een bootstrapper nodig heeft en dat uitgaat van conventies.
 
Catel combineert de mogelijkheden van Caliburn (Micro) met de eenvoud van MVVM Light
 
 
De ontwikkelaars vonden dat beide frameworks niet volledig voldeden aan de wensen van de ontwikkelaars, dus besloten ze een combinatie te maken die de ruime mogelijkheden van Caliburn(Micro) biedt, maar toch de eenvoud van MVVM Light.
 
Het prettige van Catel is dat het in delen gebruikt kan worden. Een uitstekend voorbeeld hiervan is dat het mogelijk is om alleen de view models te gebruiken in een applicatie. Wanneer men echter meer wil bereiken is het aanbevolen om ook te kijken naar de meegeleverde user controls.
Ondersteunde Platformen
Op het moment van schrijven is de nieuwste versie van Catel 1.4. Sinds deze versie ondersteund Catel niet alleen WPF en Silverlight, maar ook Windows Phone 7.
 
Het voordeel van de Windows Phone 7 implementatie van Catel is dat het veel overhead wegneemt van de ontwikkelaar. Zo is er een navigatie service waarmee eenvoudig tussen view models genavigeerd kan worden.
 
Catel ondersteund een GPS service waarmee eenvoudig GPS locaties geemuleerd kunnen worden
 
 
Ook ondersteund Catel een GPS emulatie service waarmee eenvoudig GPS locaties geemuleerd kunnen worden. Een laatste voordeel is dat tombstoning ook geintegreerd is in MVVM waardoor het bijzonder eenvoudig is om hiervoor support in een applicatie in te bouwen.
Nested User Controls Problem
 
Één van de problemen waar veel MVVM ontwikkelaars tegen aan lopen is het “nested user controls” probleem. Het probleem is dat er vaak hierarchie is in een UI die niet opgelost kan worden met één view model. Wat men vaak doet is een view model maken die weer een lijst van andere (sub) view models bevat. Echter, de master view model krijgt dan teveel functionaliteit en ook het unit testen van de functionaliteit wordt erg lastig.
 
Catel biedt hier een oplossing voor met de UserControl<TViewModel> die automatisch zorgt dat een view model aangemaakt wordt voor de control. Het bijzondere van deze user control is dat deze in staat is om de DataContext van zichzelf te injecten in de view model. Op deze manier is het dus mogelijk om een PersonControl te maken met bijbehorende PersonViewModel. De constructor van de PersonViewModel accepteert dan een IPerson instantie. Zodra de DataContext van de control een instantie van IPerson bevat zal de user control deze automatisch injecten in de PersonViewModel en vervolgens de DataContext vervangen door een instantie van de zojuist gecreëerde view model. Onder water zorgt Catel dat alles in originele staat blijft, dus als er wijzigingen komen op de DataContext zal er een nieuwe view model gemaakt worden met de nieuwe DataContext. Figure 1 geeft het probleem schematisch weer.
 
 
Figure 1: Schematische weergave “nested user controls” probleem
 
 
Met behulp van de UserControl<TViewModel> class is het mogelijk om user controls met een eigen view model te ontwikkelen
 
 
Met behulp van de UserControl<TViewModel> class is het dus mogelijk om user controls met een eigen view model te ontwikkelen.
Unit Testing
Één van de sterke argumenten om MVVM te gebruiken is de testbaarheid van loosely coupled applicaties. Catel gaat nog een stap verder door van veel services een test variant aan te bieden. Dankzij deze test varianten kan eenvoudig via een IoC container (Unity) de test variant worden gebruikt in unit tests.
 
De test implementaties van de services zorgen ervoor dat de services zelf niet gemockt hoeven te worden, al kan dat natuurlijk wel als daar behoefte aan is. Een voorbeeld van het gebruik van een test implementatie is te vinden in Listing 3. In het codevoorbeeld wordt gesimuleerd dat een gebruiker een modal dialog annuleert en de test controleert of de view model dan inderdaad geen nieuw object aan de Items property toevoegt.
 
// Check if there are no persons
Assert.AreEqual(0, viewModel.Items.Count,
    “No items expected”);
 
// Retrieve test implementation
var service = (MVVM.Services.Test.UIVisualizerService)
    viewModel.GetService<IUIVisualizerService>();
 
// Return false on next call to ShowDialog of the service
service.ExpectedShowDialogResults.Enqueue(() => false);
 
// Call “Add” command, but it should be canceled by user
viewModel.Add.Execute(null);
 
// Check if there are no persons
Assert.AreEqual(0, viewModel.Items.Count,
    “No items expected”);
 
Listing 3: Voorbeeld van een unit test
User Controls
 
Catel biedt buiten het MVVM framework ook nog user controls aan die het gemak van het ontwikkelen van software kunnen vergroten. Zo is er de DataWindow die afleidt van de Window (of in Silverlight van de ChildWindow) die automatisch veel gebruikte buttons genereert zoals OK, Cancel, Apply, maar ook eigen buttons kunnen worden toegevoegd. Ook wordt er automatisch een InfoBarMessageControl toegevoegd die de fouten overzichtelijk weergeeft voor de eindgebruiker. Op deze manier kan een ontwikkelaar zich echt concentreren op de functionaliteit in plaats van plumbing.
 
Figure 2: DataWindow met InfoBarMessageControl
 
Ook is er voor WPF een PleaseWaitWindow, een equivalent van de BusyIndicator control van Silverlight.
 
Figure 3: PleaseWaitWindow voor WPF
Toekomst
Momenteel is het team van Catel druk bezig met het realiseren van Catel 2.0. Dit wordt een grote release waarin de volgende nieuwe features ondersteund worden:
 
·         Auditing (maakt bijv. analytics mogelijk)
·         WP7 Mango support (inclusief emulatie service voor alle beschikbare sensoren)
·         MEF support
·         Service locator zodat andere IoC containers dan Unity gebruikt kunnen worden
·         en veel meer!
 
De doelstelling is om minstens eens per twee maanden een officiële release uit te brengen. Mocht je niet kunnen wachten tot de officiële release, dan is de volledige source code beschikbaar op http://catel.codeplex.com. Ook is daar meer informatie te vinden zoals documentatie, artikelen en demo applicaties.
 
Op http://catel.codeplex.com is de volledige source code inclusief documentatie, artikelen en demo applicaties beschikbaar
 
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar