30 March, 2011

WP7: “Hello World” using SimpleMvvmToolkit framework

Having been playing with a number of different frameworks for MVVM, I’ve now come to the simplest possible solution, the Tony Sneed framework called SimpleMvvmToolkit. It is presented (by Tony Sneed!) as the simplest possible way of doing MVVM. Well – that’s easy to say when you’ve made the framework, dude!

I would say (having 12 years+) experience, that it is not that easy after all, Tony! A “Getting-started” solution is presented on codeplex; but even that one is not that trivial and easy/straight forward and leaves a lot to wish for. So based on that experience, a proper “Hello world” is due!

Let’s first establish some terminology:

View (V):
The pure UI-elements. Handles rendering of UI to the end user. Has no knowledge of the ViewModel (VM) nor the Model (M).

ViewModel (VM):
UI-logic and notification of the UI when values are changed.

Model (M):
The object (e.g. Customer) to present in the UI (view). This is basically a DTO (Data Transfer Object) with little logic embedded. The properties of a Model is what is reflected in the View (using bindings).

Service:
A provider or Factory that can “provide” model-objects to the ViewModel.

ViewModelLocator (helper):
A provider of ViewModels and Services to the system.


The overall architecture of MVVM  (in the SimpleMvvmToolkit implementation) is seen here:

image

As seen in the figure – a number of classes/objects are in play here. The main players (View, ViewModel, Model and Service) are the green boxes, where as the yellow boxes are helpers. The View has as previously mentioned no knowledge of the Model or it’s properties. The “knowledge” required to render data in the View is entirely made using XAML-bindings in WPF/Silverlight. This is where the magic lies in the powerful binding implementation presented by XAML.

Hello World
The solution we are after, is the simplest possible implementation on a WP7 phone.

image             image

As seen in the solution view (above), the known players (M-V-VM) are in their own folders. In addition, a dedicated “Services” folder is presented.

Model
The model needs to inherit from ModelBase<T> (part of SimpleMvvmToolKit) to allow it to notifiy the View by means of INotifiyPropertyChanged support.

namespace MVVM_HelloWorldWP7.Models
{
public class HelloModel : ModelBase<HelloModel>
{
public HelloModel()
{
Greeting = "Hello world!";
}

private string m_greeting;
public string Greeting
{
get { return m_greeting; }
set
{
m_greeting = value;
NotifyPropertyChanged(m => m.Greeting);
}
}
}
}

ViewModel
The ViewModel is a bit more advanced. This guy needs to inherit from ViewModelDetailBase (part of SimpleMvvmToolkit). By inheriting from this baseclass and presenting the “HelloModel” as detailType, the VM will present a property called “Model” of the proper type (HelloModel in this case). As you can also see,the VM is equipped with 2 constructors; one with the Model as argument, one with a “Service” as argument. By constructing the VM with these arguments, you are actually able to inject other implementations from the outside. This is not shown in this simple example though.

namespace MVVM_HelloWorldWP7.ViewModels
{

public class HelloViewModel : ViewModelDetailBase<HelloViewModel, HelloModel>
{
#region Initialization and Cleanup

private IHelloService serviceAgent;

// Default ctor
public HelloViewModel() { }

public HelloViewModel(HelloModel model)
{
base.Model = model;
}

public HelloViewModel(IHelloService serviceAgent)
{
this.serviceAgent = serviceAgent;
}

#endregion

#region
Notifications

// TODO: Add events to notify the view or obtain data from the view
public event EventHandler<NotificationEventArgs<Exception>> ErrorNotice;

#endregion

#region
Properties

// TODO: Add properties using the mvvmprop code snippet

#endregion

#region
Methods
/// <summary>
///
ICommand to be called by declaration in the view (Click to load-button)
/// </summary>
public ICommand LoadDataCommand
{
get
{
return new DelegateCommand(LoadHelloModel);
}
}
public void LoadHelloModel()
{
this.Model = serviceAgent.GetModel();
}

#endregion

#region
Completion Callbacks

// TODO: Optionally add callback methods for async calls to the service agent

#endregion

#region
Helpers

// Helper method to notify View of an error
private void NotifyError(string message, Exception error)
{
// Notify view of an error
Notify(ErrorNotice, new NotificationEventArgs<Exception>(message, error));
}

#endregion
}
}

Service
The service presenting the VM with Models, consists of a declaration in the form of an Interface (good design!) as well as an implementation (mock in this case). The Service here was what confused me in the beginning; it is not a service in it’s traditional sense, but should be considered in a more broad terminology as a (simple) provider of Models. It does so by asking database, web services or other means of logic. Ultimately, it will present the VM with an instance of a Model (HelloModel in our example).

namespace MVVM_HelloWorldWP7.Services
{
public interface IHelloService
{
HelloModel GetModel();
}
}
namespace MVVM_HelloWorldWP7.Services
{
public class HelloServiceMock : IHelloService
{
public HelloModel GetModel()
{
return new HelloModel();
}
}
}


ViewModelLocator
The ViewModelLocator is what ties the entire MVVM-system together. Hence, it’s a quite important part of the entire setup here. The ViewModelLocator is responsible for presenting VM’s to the system when they are requested.

namespace MVVM_HelloWorldWP7
{
public class HelloWorldModelLocator
{

private IHelloService serviceAgent;

/// <summary>
///
Initializes a new instance of the ViewModelLocator class.
/// </summary>
public HelloWorldModelLocator()
{
//init the service (agent)
serviceAgent = new HelloServiceMock();
}

private HelloViewModel m_helloVM;
/// <summary>
///
Gets the HelloViewModel.
/// </summary>
public HelloViewModel HelloViewModel
{
get { return m_helloVM ?? (m_helloVM = new HelloViewModel(serviceAgent)); }
}
}
}

To allow the components of the system to gain access to the single ViewModelLocator in the system, it is declared in AppResources (App.xaml). Note the x:key=”Locator”; this is a convention in SimpleMvvmToolkit and can not be changed. As the ViewModelLocator has a default constructor, it can be instantiated by WPF automatically and presented to the rest of the system at will.


Press image to see larger version


View (part 1)
The final part of this system is the UI (the View). This is pure XAML and declarations. It knows (luckily) nothing about anything but how to render UI elements. As seen, you need of-course to set the datacontext to the ViewModelLocator (remember the x:key=”Locator” above ?)


Press image to see larger version


With this context set, you can now reference the various display fields like this (Model.<propertyName>).


Press image to see larger version


View (part 2)
To make this example a little more than just display a text, support for Click events of a button is also presented. This is wired up using declarations and attached properties like below. It does however mean, that the UI is completely free of code as this will link Click events to the ViewModel directly.


Press image to see larger version


To make these aliases work, you need to add a reference to them at the top of the page


Press image to see larger version


So – finally it is working and everybody are happy! Not that simple after all…
But, hopefully someone gains from this as well.


Download the above sample here: www.clauskonrad.net/download.ashx?id=14


Technorati Tags: ,,

8 comments:

Anonymous said...

Clearest explanation I've come across. I'm now going to try it out (once I can get the kids to bed :-)).

Thank you.

Anonymous said...

I'm struggling to read the images - can you change them for code snippets please?

Anonymous said...

Hi again,

I've followed through your code but am getting an error.

System.Windows.Data Error: BindingExpression path error: 'SummaryVM' property not found on '(StaticResource Locator)' 'System.String' (HashCode=1761361087). BindingExpression: Path='SummaryVM' DataItem='(StaticResource Locator)' (HashCode=1761361087); target element is 'mynamespace.Screens.Summary' (Name=''); target property is 'DataContext' (type 'System.Object')..

Help - I've not been able to fix this.

Dave.

Claus Konrad said...

Hi

Thanks a lot for your nice comments. The reason you are seeing this error is bacause you are missing a property on your ViewModelLocator (guessing from the error message?). It is the ViewModelLocator that provides the rest of the system with instances of ViewModels as well as Services. That is why it is so important to have this one (Locator) right in the first place.

I've added a download link in the post above to allow you to download the sample.
Thanks!

Anonymous said...

Hi Claus
Using your download I was able to fix my errors (mainly using wrong time of brackets in places). Now I'm trying to persist the data across screens.
Dave.

Claus Konrad said...

Well - that's grand.
Persisting cross screens is what (in other contexts) a singleton in-memory storage is for. Or depending on the size, just pass it in as argument during navigation (the navigationcontroller supports this). BUT, it is running on a contrained device (phone), hence pay attention to the memory consumption of the application. If too large - persist to disk (using IsolatedStorageSettings.ApplicationSettings.Save(stuff...)

Thiago Fernandes said...

Conrad, I tested your application using NUnit for integrate Windows Phone 7 Mvvm and Unit Test with successful

Thiago Fernandes said...

I tested you application successfully. I created an application test using NUnit and your application. Do you create more examples using Mvvm for WP7?

iPhone/XCode - not all cases are equal!

This bit me! Having made some changes to an iPhone application (Obj-C); everything worked fine in the simulator. But, when deploying the s...