Click here to Skip to main content
16,017,944 members
Articles / Operating Systems / Windows Phone

Windows Phone TDD Part 4: The Proper Way To Do MVVM

Rate me:
Please Sign up or sign in to vote.
4.50/5 (5 votes)
24 Feb 2014CPOL3 min read 15K   1   5
Windows Phone TDD Part 4: The proper way to do MVVM

A few days ago, a friend asked me if you should put the business logic in the Models and the ViewModels from MVVM. Basically, my answer was NO, HELL NO!

I will talk about just two of the reasons why I think this.

The first one is that you will have to rewrite your business logic for every app where you will use it. Let’s say that you have a client that wants an app for handling his account, so you make him a desktop app with WPF. You use MVVM, and you have your logic there, but what do you do when the client wants a Windows Phone app? The logic is in the ViewModels from the WPF app, so now you have to rewrite it again for the Windows Phone app. Wouldn’t it be better if the business logic was in some Portable Class Library that you could reuse for every platform instead of binding it with the ViewModels? I thought so.

The second reason is that I think the ViewModels should handle some of the UI logic as well. If you’re doing TDD, then you will want to test as much of your code as possible. If the ViewModels don’t contain the business logic, what’s stopping you from using them to control your UI? Imagine the following scenario, you have an app that shows a popup with a login form. When the user clicks on the login button, and he is logged in, you want the popup to dissappear. The problem is that you don’t know if the login was successful or not, so you can’t just close the popup when the button was pressed. So how do you make the View wait for an answer from the ViewModel? Or better, how do you write the code for this task so you could test it?

If you’re using Caliburn.Micro, you could bind the Visibility property of the popup to a boolean property in the ViewModel. The code for the login function could be like this:

C#
public async Task Login(string username, string password)
{
    var loginResult = await LoginUser(username, password);
    if (loginResult)
        LoginPopupIsOpened = false;
    else MessageBox.Show("An error occurred");
}

Because the popup visibility is binded to our property, then when I will set it to false, the popup visibility will be set to Collapsed. The upside here is that I can write a test for this very easily.

C#
[Test]
public void successful_login_should_close_popup()
{
    var viewModel = new LoginViewModel();
    viewModel.LoginPopupIsOpened = true;
    BindAuthenticationServiceToSuccess(); //we bind the login function to return true
    await viewModel.Login();
    viewModel.LoginPopupIsOpened.Should().BeFalse();
}

If you want a high code coverage, try to make your ViewModel handle the UI, but don’t do it directly, so don’t use objects that are related to the UI (Button, Grid, TextBox, etc… or properties like Visibility, Text, etc.). You can see that I haven’t used the Visibility enum above, that’s because I want to keep the UI related stuff out of my ViewModel. So I’m binding Visibility to boolean properties, Image source Uri to string properties, events to functions, and so on. The rule is to keep all your functionality in the ViewModel, and the strictly UI stuff in the View. So far, this is working great for me, especially because I’m using Caliburn which makes the separation between the View and the ViewModel very easy.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Feel IT
Romania Romania
Hey! I'm Bogdan Bujdea, a software developer from Romania. I started to learn programming since my first year of highschool with C++. During my time in college I tried various languages like Java, C#, Haskell, Javascript, Python, etc. but my favorite remains C#.
I started my career with Thinslices as a Windows 8 developer, but now I work for Feel IT Services as a Windows Phone developer.

Comments and Discussions

 
QuestionModel != ViewModel Pin
William E. Kempf6-Mar-14 7:16
William E. Kempf6-Mar-14 7:16 
AnswerRe: Model != ViewModel Pin
Bogdan Bujdea9-Mar-14 6:37
Bogdan Bujdea9-Mar-14 6:37 
GeneralRe: Model != ViewModel Pin
William E. Kempf10-Mar-14 7:49
William E. Kempf10-Mar-14 7:49 
Bogdan Bujdea wrote:
If you put the business logic in the models, you won't be able to use them everywhere.


Why ever not? What's in your Model if it's not business logic?

Bogdan Bujdea wrote:
How are you going to use models from a WP app in a console app?


If the Models are in a PCL they can be used from any application. I don't see what you think the issue is here?

Bogdan Bujdea wrote:
I don't need models that implement INotifyPropertyChanged in my business logic, because my business logic should not care if it's a desktop app, mobile app, etc.


Who said anything about putting INPC in your Models? Regardless, why would it matter if you did? INPC is supported by the PCL and thus is supported in all of the apps you mention. I generally use INPC only in the ViewModels, not the Models, but you aren't doing anything wrong or preventing reusability if you use INPC in your Models.

Bogdan Bujdea wrote:
If you put your ViewModels in PCLs then you won't be able to reuse them in another platform, because the ViewModels could have another behaviour. In my Windows Phone app I could have a ViewModel for a login page and a ViewModel for some feed, but in my Windows 8 app I could show the login and the feed in the same page, so it won't help you if you put them in a PCL.


Totally wrong. ViewModels don't care a thing about the View implementation or presentation. It doesn't matter if your LoginView and your FeedView are pages or controls, so they are reusable in all of the scenarios you describe. I'm not talking theory here, I've done this and there are several examples on the web where others have as well. It does help to have the ViewModel in the PCL, as it means you implement and test only once. Code duplication is always a bad thing, and in general using the excuse that you duplicated code because it's a different platform isn't much of an excuse any more.
William E. Kempf

GeneralRe: Model != ViewModel Pin
Bogdan Bujdea10-Mar-14 9:20
Bogdan Bujdea10-Mar-14 9:20 
GeneralRe: Model != ViewModel Pin
William E. Kempf10-Mar-14 9:46
William E. Kempf10-Mar-14 9:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.