Windows Phone 7 (WP7) is een nieuw en snel groeiend platform met een achtergrond in de bewezen Microsoft technologieën Silverlight en XNA. Het Model-View-ViewModel design pattern (MVVM) is een hot item in de Silverlight community en is zich ook voor Silverlight for Windows Phone aan het bewijzen als het design pattern voor ontwikkeling. Naast veel voordelen brengt dit design pattern ook een aantal nieuwe uitdagingen met zich mee. Frameworks kunnen hier een uitkomst bieden. Frameworks geven het gemak van een bewezen oplossing voor bestaande uitdagingen, zonder zelf het wiel opnieuw te hoeven uitvinden. In dit artikel worden drie populaire MVVM-frameworks, geschikt voor WP7 ontwikkeling, met elkaar vergeleken.
MVVM & WP7
Silverlight is een opvallende technologie in de zin dat je er web gebaseerde applicaties mee kunt maken, maar ook out-of-browser applicaties. Tijdens de evolutie van Silverlight is in de community getracht om bewezen design patterns te volgen.
Vanuit de web gerelateerde technologieën heeft het Model-View-Controller (MVC) pattern zich bewezen en het is nog steeds immens populair bij developers. MVC is echter minder geschikt voor een Silverlight oplossing en dit is waar het Model-View-ViewModel (MVVM) pattern naar voren komt. Het heeft hetzelfde doel als MVC, maar maakt gebruik van de unieke mogelijkheden van Silverlight, zoals data-binding, validatie en XAML om een complexe visuele presentatie laag te maken. Een belangrijk doel is separation of concerns (SoC), waarin de code gescheiden is in verschillende lagen, elk met hun eigen rol en verantwoordelijkheden.
Ook Silverlight for Windows Phone is gebaat bij het gebruik van MVVM. Het brengt echter een aantal nieuwe uitdagingen met zich mee. De huidige release van Windows Phone maakt gebruik van versie 3 van Silverlight, de meest recente versie van Silverlight voor de desktop is versie 4. Functies zoals Commands zijn (nog) niet ondersteund in Silverlight versie 3, terwijl deze juist nodig zijn om methoden vanuit de View in de ViewModel layer aan te spreken. De volgende release van Windows Phone (codenaam Mango) zal Silverlight versie 4 draaien.
Frameworks bieden o.a. oplossingen voor communicatie tussen onderdelen van applicaties en bieden de mogelijkheid om sneller te ontwikkelen door middel van het inschakelen van modules die veelvuldig gebruikt worden. In dit artikel kijken we naar mogelijkheden van de frameworks MVVM Light, Caliburn.Micro en Prism4.
MVVM Light
MVVM Light is een framework ontwikkeld door Laurent Bugnion. De belangrijkste feature van dit framework is ‘Blendability’. Dit is de mate waarin het mogelijk is om Microsoft Expression Blend te gebruiken bij het ontwikkelen van een applicatie. Er kan bijvoorbeeld een preview van de XAML UI gegenereerd worden tijdens het ontwerpen van de applicatie. Expression Blend heeft (nog) geen ondersteuning voor ViewModels met parameterloze constructors. Zowel Expression Blend als Visual Studio probeert de data-context van objecten in het XAML-bestand tijdens design time te laden. Wanneer dit voorkomt zullen er fouten in design time ontstaan die de preview mogelijk verstoren, of de tools zelfs doen crashen. Het MVVM Light framework maakt het onder andere mogelijk om design time data te gebruiken en zo de applicatie te visualiseren (met ingevulde data) tijdens het ontwikkelen.
Caliburn.Micro
Caliburn.Micro is een zogeheten ‘opinionated framework’ ontwikkeld door Rob Eisenberg. Een opinionated framework, letterlijk vertaald een ‘koppig raamwerk’, wil zeggen dat er vanuit de auteur een oplossing is bedacht en dat deze aanpak leidend is voor het framework. Andere uitwerkingen zijn mogelijk, maar dan komt de oplossing van het framework niet tot zijn recht. Verder vergemakkelijkt het framework het ‘configureren’ van presentatielagen op XAML platformen. "Convention over configuration" is het motto van het framework, wat in het kort betekend dat er van tevoren vastgestelde regels zijn die bijvoorbeeld de Views en ViewModels aan elkaar koppelen. Als je volgens deze conventies werkt, dan hoef je niets zelf te configureren of coderen om het mogelijk te maken. Deze conventies kunnen gemakkelijk uitgebreid of uitgeschakeld worden. Het framework gebruikt verschillende onderdelen om SoC mogelijk te maken.
Prism 4
Prism4 is in feite een verzameling van patterns en best practices. De documentatie en herbruikbare code (de Prism Library) bieden variaties van loosely coupled code en maakt SoC mogelijk. Prism is in combinatie met het MVVM pattern in te zetten, maar Prism is meer dan alleen een MVVM-framework. Binnen Prism 4 is een library beschikbaar voor Windows Phone 7.
Aan de slag met de frameworks
In Visual Studio 2010 zijn alle drie de frameworks beschikbaar als NuGet package. Je kunt NuGet gebruiken om de frameworks aan je project toe te voegen. Dit kan op twee manieren:
Via de NuGet Package Manager Console:
PM> Install-Package Caliburn.Micro
PM> Install-Package MvvmLight
PM> Install-Package Prism
Of via de UI, de search box bovenin rechts gebruikend, zie figuur 1.

Figuur 1
Vergelijking van leercurve, complexiteit en populariteit
Voor zowel de leercurve en de complexiteit staan MVVM Light en Prism4 ongeveer op een gelijk niveau. De leercurve is kort, omdat de frameworks niet erg complex zijn. Wat ze doen, doen ze wel goed. Caliburn.Micro is in vergelijking met deze twee een stuk complexer en moeilijker om te leren. De leercurve is deels afhankelijk van de ervaring van de ontwikkelaar. Als je functies als Tombstoning of View Caching al eerder gebruikt hebt, zal het niet moeilijk zijn om te begrijpen hoe Caliburn.Micro ze toepast.
Caliburn.Micro is ook het minst populair. Per 15 augustus ’11 geven de download cijfers op CodePlex aan dat deze ‘maar’ 17.000 keer gedownload is, tegen 55.000 van MVVM Light en 215.000 van Prism. Hierbij moet vermeld worden dat Caliburn.Micro en MVVM Light pure MVVM frameworks zijn en dat Prism veel meer is dan een MVVM framework. Het is echter moeilijk om te bepalen welk gedeelte van de gebruikers de frameworks gebruiken voor MVVM én WP7, gezien deze libraries onderdeel zijn van dezelfde download.
Vergelijking van de eigenschappen
In de documentatie, op de websites en in de lezingen van de auteurs (van MVVM Light en Caliburn.Micro) wordt aangegeven welke eigenschappen de frameworks bevatten. Om het de lezer makkelijker te maken hebben we een samenvatting gemaakt van de eigenschappen per framework, zie tabel 1.
Eigenschappen
|
MVVM Light
|
Caliburn.Micro
|
Prism
|
Blendability
|
X
|
|
|
Binding aan non-framework elementen
|
X
|
|
X
|
Framework events aan ICommands binden
|
X
|
X
|
|
Operaties verzenden naar de UI thread
|
X
|
X
|
X
|
ViewModelBase met INotifyPropertyChanged
implementatie
|
X
|
X
|
|
Relay / Delegate Commands
|
X
|
X
|
X
|
Messenger
|
X
|
X
|
X
|
Application Bar Commanding support
|
|
X
|
|
Bootstrapper
|
|
X
|
|
Conventions
|
|
X
|
|
ViewModelLocator
|
X
|
X
|
|
ViewLocator
|
|
X
|
|
Logging
|
|
X
|
|
NavigationService
|
|
X
|
|
Tombstoning support
|
|
X
|
|
Coroutines
|
|
X
|
|
View Caching
|
|
X
|
|
Tabel 1. Eigenschappen van de Frameworks

Figuur 2: MVVM
ViewModels en ViewModelBase
De komende onderwerpen, ViewModelBase, ViewModelLocator en Messaging, zijn in elk framework beschikbaar. Om de vergelijking te maken worden eerst de kernbegrippen toegelicht.
ViewModels zijn de koppeling tussen de View en de Model laag zoals gevisualiseerd in figuur 2.
Ze bevatten (bijna) alle logica die niet in de View of Model laag thuis hoort en worden gebruikt om de INotifyPropertyChanged interface te implementeren. Deze interface bevat een PropertyChanged event, waardoor data-bindings op de hoogte worden gesteld van veranderingen in het ViewModel.
Elk ViewModel zou INotifyPropertyChanged moeten implementeren, zo worden wijzigingen in de View of in het Model aan data-bindings gecommuniceerd. Het is gebruikelijk om een ViewModelBase class aan te maken met een implementatie van INotifyPropertyChanged, van welke elk ViewModel de implementatie overerft. Verder is het mogelijk om alle overige gedeelde functies onder te brengen in de ViewModelBase, bijvoorbeeld validatie. Om validatie mogelijk te maken kun je in de ViewModelBase INotifyDataErrorInfo implementeren, wat op eenzelfde manier werkt als INotifyPropertyChanged.
De drie frameworks leveren allen een standaard ViewModelBase die direct geïmplementeerd kan worden. Niet alleen hebben we dus een ViewModelBase die ons werk makkelijker maakt, we hoeven hem niet eens zelf te schrijven met deze frameworks. Bij Caliburn.Micro zit dezelfde functionaliteit in de Screen klasse, zodat ook bij Caliburn.Micro de INotifyPropertyChanged implementatie kan worden overgeërfd. De Screen klasse is in dit geval een andere naam voor ViewModelBase.
ViewModelLocator
De ViewModelLocator is een oplossing om ViewModels te koppelen aan de bijbehorende View. Bij MVVM Light configureer je in de ViewModelLocator een property waarmee je het ViewModel beschikbaar maakt. Vervolgens stel je deze property in als datacontext van je View. In Caliburn.Micro werkt de ViewModelLocator met een naamgevingsconventie, elke View heeft een ViewModel met dezelfde naam als prefix, bijvoorbeeld ProductView en ProductViewModel. Deze naamgevingsconventie maakt automatisch een koppeling tussen View en ViewModel.
De ViewModelLocator is een specifieke implementatie van het Service Locator pattern. Dit pattern wordt gebruikt om classes te ontkoppelen van hun afhankelijkheden, zodat deze afhankelijkheden kunnen worden aangepast zonder dat de classes gewijzigd hoeven worden. Verder kan het gebruikt worden om logica te schrijven als de concrete implementatie niet bekend is tijdens compile time.
Het grote voordeel van de ViewModelLocator is dat er geen sterke afhankelijkheid in de XAML van de View nodig is om een specifiek ViewModel te binden. De binding in de View gaat direct op de ViewModelLocator die op zijn beurt het juiste ViewModel zoekt.
Tot slot bieden ViewModelLocators ook een extra entry point voor activatie, deactivatie, disposing en inactivity logica. Door event handlers in de App.xaml te gebruiken, die gekoppeld zijn aan deze states van de applicatie of ViewModels, is het mogelijk om inactieve ViewModels te disposen of Tombstoning te regelen. Het disposen van een ViewModel is wenselijk, rekening houdende met de beperkte beschikbaarheid van resources op WP7 devices.
Messaging
Alle drie de frameworks hebben support voor Messaging. Hiermee kunnen op een makkelijke en loosely-coupled manier berichten of objecten van een deel van de MVVM applicatie naar een ander deel van de applicatie gestuurd worden. De noodzaak hiervoor is ontstaan uit het opsplitsen van de MVVM-applicatie in Views met afzonderlijke ViewModels. Dit biedt goede mogelijkheden qua SoC en loose coupling, maar zorgt er ook voor dat bijvoorbeeld ViewModels niet makkelijk met elkaar kunnen communiceren.
Bij alle drie de frameworks wordt hiervoor een implementatie van het Observer pattern gebruikt. Hierdoor kunnen ViewModels communiceren via een Publish & Subscribe mechanisme. De flow hierbij wordt dan zoals weergegeven in figuur 3.

Figuur 3
- BarViewModel en BazViewModel abonneren zich bij een message-bus op berichten (van een bepaald type).
- FooViewModel publiceert een bericht naar de message-bus.
- BarViewModel en BazViewModel ontvangen het bericht van de message-bus.
Het grote voordeel hiervan is dat ViewModels elkaar niet hoeven te ‘kennen’. FooViewModel weet niet van het bestaan van BarViewModel of BazViewModel en vice versa. Het enige dat een ViewModel hoeft te weten is dat het kan abonneren op berichten bij een message-bus of berichten kan publiceren naar een message-bus.
Bij Prism werkt dit in de praktijk zoals weergegeven in code snippet 1, waarbij hier is uitgegaan van FooViewModel die een Person object verstuurt.
namespace PrismMessagingApp.ViewModels
{
public class FooViewModel
{
private EventAggregator _eventAggregator;
public BarViewModel(IEventAggregator eventAggregator);
{
this._eventAggregator = eventAggregator as EventAggregator;
private Person _CurrentPerson;
public Person CurrentPerson
{
get
{
return _CurrentPerson;
}
set
{
if (value != _ CurrentPerson)
{
_ CurrentPerson = value;
OnPropertyChanged("CurrentPerson");
CompositePresentationEvent<Person>_currentPersonEvent =
_eventAggregator.GetEvent<CompositePresentationEvent<Person>>();
Person msg = CurrentPerson;
_currentPersonEvent.Publish(msg);
}
}
}
}
}
}
Code snippet 1
Het ontvangende ViewModel BarViewModel heeft de code zoals te zien in code snippet 2.
Met behulp van de EventAggregator (messaging klasse) wordt in dit geval een Person-object verstuurd van FooViewModel naar BarViewModel, zonder dat deze ViewModels aan elkaar refereren.
namespace PrismMessagingApp.ViewModels
{
public class BarViewModel
{
public FooViewModel(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<CompositePresentationEvent<Person>>()
.Subscribe(OnMsgReceived);
}
public void OnMsgReceived(Person msg)
{
CurrentPerson = msg;
}
private Person _CurrentPerson;
public Person CurrentPerson
{
get
{
return _CurrentPerson;
}
set
{
if (value != _ CurrentPerson)
{
_ CurrentPerson = value;
OnPropertyChanged("CurrentPerson");
}
}
}
}
}
Code snippet 2
MVVM Light en EventToCommand
Bij MVVM Light is één van de extra interessante features de zogenaamde EventToCommand behavior. Dit is een Expression Blend behavior. De behaviors zijn geoptimaliseerd voor Expression Blend, maar kunnen ook direct toegevoegd worden in de XAML. De EventToCommand behavior wordt gebruikt om in XAML een event aan een ICommand te binden.
Een simpel voorbeeld hiervan is een Button Click event binden aan een Command GoDoSomething in FooViewModel middels de XAML-markup in FooView zoals weergegeven in code snippet 3.
Wanneer de Button ingedrukt wordt, dan de GoDoSomething methode van het FooViewModel uitgevoerd. Het mooie aan EventToCommand is dat het aan alle soorten framework elementen toegevoegd kan worden. Het is dus mogelijk om elk denkbaar event te koppelen aan elk gewenste command.
<Button x:Name="GoDoSomethingButton" Content="This Button Does Something">
<Custom:Interaction.Triggers>
<Custom:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand
x:Name="GoDoSomethingButtonClicked"
Command="{Binding GoDoSomething}" />
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</Button>
Code snippet 3
Caliburn Micro en Conventions
Caliburn.Micro is voorzien van features die het leven van de programmeur een stuk makkelijker maken. De krachtigste feature is het gebruik van conventies. Zoals in de beschrijving van Caliburn.Micro staat aangegeven, werkt Caliburn.Micro met het principe dat Convention over Configuration heet. Hierbij wordt gebruik gemaakt van afspraken voor veel voorkomende taken en namen in applicaties.
Standaard zijn er een aantal conventies actief wanneer Caliburn.Micro gebruikt wordt. Deze conventies zorgen er voor dat de View en het volgens de conventie bijbehorende ViewModel aan elkaar gebonden worden. De ViewModelBinder.Bind methode stelt automatisch de DataContext in en het target van ActionMessages. De ViewModelBinder heeft als belangrijke eigenschap het automatisch opzetten van property bindings en actions. Hiervoor wordt de UI in de View nagezocht op properties en actions en worden deze vergeleken met de properties en methoden in het ViewModel. Wanneer hierbij een match wordt gevonden, worden de bindings automatisch gemaakt.
Een simpel voorbeeld hiervan is het aanmaken van een Button genaamd GoDoSomething. Wanneer in het bijbehorende ViewModel een methode wordt gebruikt met dezelfde naam, wordt automatisch een binding naar deze methode gemaakt op het Click event van de button. Een toevoeging daarop is de check op eventuele guard conditions. Een voorbeeld van een dergelijke guard condition:
wanneer in het voorgaande voorbeeld in het ViewModel ook een "CanGoDoSomething" property aanwezig is, wordt deze automatisch gekoppeld aan de "enabled" state van de "GoDoSomething" button.
Al deze conventies werken zonder dat er enige CodeBehind nodig is in de View en kunnen dus getest worden. De XAML-markup van de Button is hierdoor simpel en bevat geen referenties of bindings naar het bijbehorende ViewModel.
De standaardconventies in Caliburn.Micro kunnen naar eigen wens aangepast worden via de ConventionManager. Wanneer je in bovenstaand voorbeeld geen actions wilt binden aan het Click event van een button, maar aan het MouseEnter event, kan dat door in de ElementConventions de regel aan te passen zoals te zien in code snippet 4.
//Aanpassing van de ElementConvention
AddElementConvention<ButtonBase>(ButtonBase.ContentProperty, "DataContext", "Click");
//Naar:
AddElementConvention<ButtonBase>(ButtonBase.ContentProperty, "DataContext", "MouseEnter");
Code snippet 4
Voor elementen waar nog geen conventie voor aanwezig is in het framework, zoals elementen uit de Silverlight Toolkit, kun je conventies toevoegen. In code snippet 5 een voorbeeld van hoe je een dergelijke conventie aanmaakt.
AddElementConvention<AutoCompleteBox>(AutoCompleteBox.ItemsSourceProperty,
"SelectedItem",
"SelectionChanged")
.ApplyBinding = (viewModelType,
path,
property,
element,
convention) => {
ConfigureSelectedItem(element,
AutoCompleteBox.SelectedItemProperty,
viewModelType,
path);
return true;
};
Code snippet 5
De parameters van de AddElementConvention<1>(2, 3, 4) zijn:
1>
- Het type element waar de conventie op toegepast wordt
- De property van het element waar de conventie aan databind
- De context voor de data-binding
- Het event waar actions aan gewired worden
Wanneer je geen gebruik wenst te maken van conventies kan ViewModelBinder.ApplyConventionsByDefault op false gezet worden.
Caliburn Micro & Logging
Omdat conventies krachtig zijn, is ook de kans op fouten hoger als een conventie niet goed werkt. Caliburn.Micro beschikt over een Logging functie, waarmee alle acties die het Caliburn.Micro framework doet bijgehouden worden. Hiermee kan precies nagegaan worden of conventies zich op de juiste manier gedragen.
De makkelijkste manier om snel een Debug of Trace Logger toe te voegen is door via NuGet het "Caliburn.Micro.Logging" package te installeren. Wanneer dit is gedaan, kan je de code toevoegen in de door Caliburn.Micro aangemaakte AppBootstrapper klasse in de root van de applicatie, zoals te zien is in het eerste deel van code snippet 6, de bijbehorende Visual Studio Debug-output staat tot slot ook vermeld.
Hierin zien we dat Caliburn.Micro door het gebruik van conventies de View en het ViewModel gekoppeld heeft, de DataContext automatisch is gezet en dat de Button GoDoSomething gekoppeld is aan de actie GoDoSomething in het ViewModel. Vooralsnog is Caliburn.Micro het enige framework dat deze koppelingen automatisch kan maken, MVVM Light en Prism bieden hiervoor geen mogelijkheden.
//Code snippet 6
LogManager.GetLog = type => new DebugLogger(type);
//Of
LogManager.GetLog = type => new TraceLogger(type);
//Hierbij wordt in de Visual Studio Debug-output de volgende log gecreëerd
INFO: [2011-07-25T15:44:14.8020551+02:00] Binding CaliburnConventionsApp.FooView and CaliburnConventionsApp.FooViewModel.
INFO: [2011-07-25T15:44:14.8380572+02:00] Setting DC of CaliburnConventionsApp.FooView to CaliburnConventionsApp.FooViewModel.
INFO: [2011-07-25T15:44:14.8400573+02:00] Attaching message handler CaliburnConventionsApp.FooViewModel to CaliburnConventionsApp.FooView.
INFO: [2011-07-25T15:44:14.8910602+02:00] Action Convention Applied: Action GoDoSomething on element GoDoSomething.
INFO: [2011-07-25T15:44:14.9740649+02:00] Action Convention Not Applied: No actionable element for ToString.
INFO: [2011-07-25T15:44:14.9760651+02:00] Action Convention Not Applied: No actionable element for Equals.
INFO: [2011-07-25T15:44:14.9770651+02:00] Action Convention Not Applied: No actionable element for GetHashCode.
INFO: [2011-07-25T15:44:14.9780652+02:00] Action Convention Not Applied: No actionable element for GetType.
INFO: [2011-07-25T15:44:15.0150673+02:00] Action: GoDoSomething availability update.
INFO: [2011-07-25T15:44:17.2501951+02:00] Invoking Action: GoDoSomething.
Code snippet 6
Links
Conclusie
MVVM is een design-pattern dat door een groot aantal ontwikkelaars toegepast word. Zodoende is er ook een groot aanbod aan frameworks geënt op het toepassen van dit pattern. Als we kijken naar wat de drie gekozen frameworks ons bieden moeten we rekening houden met deze brede achtergrond aan ontwikkelaars die aan de frameworks gewerkt hebben of er gebruik van maken. Elk framework heeft namelijk zijn eigen plus- en minpunten. Zo is Caliburn.Micro een framework wat gecompliceerde problemen oplost en complexe features te bieden heeft. Voor kleinere applicaties die minder complexiteit vereisen zijn de kleinere frameworks beter inzetbaar, zoals MVVM Light en Prism. Omdat Caliburn.Micro een opinionated framework is kun je dit framework beter niet kiezen als je een andere aanpak wenst.
Als de overdaad aan features van Caliburn.Micro precies zijn wat je zoekt, is het absoluut één van de betere frameworks. De conventies alleen al zijn, zodra je eraan gewend bent, niet meer weg te denken en nemen veel werk uit handen. Zeker voor de leergierige ontwikkelaar is het een interessant stuk code. Het is echter niet weggelegd voor de beginner en vereist een flinke verdieping als je er alles uit wilt halen.
Zowel MVVM Light als Prism 4 zijn uitstekende frameworks om snel op te pakken en snel applicaties mee te bouwen. MVVM Light blinkt uit in sterke community support (waar de auteur van het framework een flink aandeel in heeft) en Prism moet het meer hebben van de best practices. Bij Prism kan wel gezegd worden dat alle features als zelfstandige onderdelen toe te passen zijn. Bij MVVM Light en Caliburn.Micro vormen de onderdelen één geheel, het ene onderdeel werkt niet naar behoren zonder het andere. Prism is namelijk niet puur MVVM, zoals MVVM Light en Caliburn.Micro dat wel zijn. MVVM Light en Caliburn.Micro leunen op de toepassing van het MVVM pattern, zodoende zijn de onderdelen niet zelfstandig toe te passen. Bijvoorbeeld de EventAggregator van Prism kan gebruikt worden in een willekeurige applicatie, de ViewModelLocator van MVVM Light en Caliburn.Micro is echter overbodig als je geen ViewModels gebruikt.
Tot slot een tip die geldt voor alle drie de frameworks. Vergeet niet voor ondersteuning bij de documentatie op CodePlex te kijken. Er staan verschillende links die zowel de basisprincipes uitleggen, alsook praktische voorbeelden.
Over de auteur
Lesley Barnier en Mike Beerman zijn beiden Solution Developer bij Avanade (www.avanade.com) met een sterke focus op Windows Phone 7 development.