Sunday, December 8, 2013

Building a cross platform solution for Windows Phone and Windows 8. Part III: We need a pattern.

Other parts in this series:

Part 0: Intro
Part I: Quick sharing of code
Part II: The class library approach
Part III: We need a pattern
Part IV: Mocking out the differences
Part V: Event to command
Part VI: Behaviors for coping with orientation changes
Part VII: Tombstoning

In the previous part I talked about portable class libraries and mentioned the need for a pattern in our code. This pattern will be the Model - View - ViewModel pattern, or MVVM for short. It divides the pieces of your application in more sensible parts and allows you to better define the responsibilities of each individual part. The View for instance will be responsible for nothing more than UI related code. This will be your XAML file. When using MVVM, I try to define my entire user interface in the XAML of my application. I hardly ever put anything in the code behind (except maybe for some UI specific code), all application logic will go someplace else.

The Model in this pattern can be either data you need or services you will be using. These will contain quite some logic and we can write them in such a way that they are unit testable.




The ViewModel will sit between the View and the Model and its responsibility will be to shape the data of the Model in a way that is specific for a certain View. As with the Model, it will also be unit testable.

The ViewModel will contain all the properties for the View to databind to. For these properties you can choose to fire the PropertyChanged event - and thus your ViewModel will implement INotifyPropertyChanged.



The above piece of code shows two properties of one of my viewmodels. The first one is for a collection of items. The second one is for a simple property. Both properties fire the PropertyChanged event. As you can see this is done through a lambda expression. I prefer putting this kind of PropertyChanged event wrapping in a base class for my viewmodels and providing the easier lambda syntax. This provides me with compile time checking (and less runtime errors when I make typos). The way to do this, is shown in the following code example.




Next to simple data properties, your ViewModel can also contain command properties. The View can also databind to these (for instance from the Command property of a Button). A command property will ascertain something gets executed in the ViewModel or in the Model.



As you can see, I use a RelayCommand class, which again makes the creation of command properties easier. The RelayCommand class will implement the ICommand interface and will provide an API by which you can createICommands based on lambdas or delegates. The RelayCommand class looks like this:



When creating commands for your view to databind to, you can also use the CanExecute property, to indicate when a command can be fired, and thus, when the button you databind do should be enabled. This is quite a neat way to automatically enable or disable buttons on your view.



Databinding to all this, is now quite easy from the view.



Using this pattern we get a better separation of concerns in our application. The only thing to watch out for is the danger of your ViewModels getting too big. Keep in mind that the ViewModels' purpose is to shape the data of the Model specifically for a certain View. This means that most of the logic will be the responsibility of the Model, and not necessarily of the ViewModel.

The advantage will be that you can put both Model and ViewModel in your portable class library, giving you the ability to reuse all of the logic between different platforms. And since both Model and ViewModel can be unit tested, you gain a great advantage.



But we will still have to add some extra techniques if we want the ViewModel to be able to use platform specific services. For this we will introduce dependency inversion in the next part of this series.

Other parts in this series:

Part 0: Intro
Part I: Quick sharing of code
Part II: The class library approach
Part III: We need a pattern
Part IV: Mocking out the differences
Part V: Event to command
Part VI: Behaviors for coping with orientation changes
Part VII: Tombstoning

No comments:

Post a Comment