Mvvmcross is a MVVM
platform that solves a lot of issues that you will face sooner or later while developing for cross platforms (such as Android, Windows Phone or IOS).
The problem is, that every platform has its own specifications, that's why you cannot simply write one code and run it on multiple platforms as is. That’s where Xamarin becomes very handy.
The main feature of mvvmcross is that you are separating your logic not only from the views (as in MVVM
and MVC
patterns), but from specific platform implementation.
Your business logic will be implemented in your PCL (Portable Library Class), and each platform can reference it as its logic, while implementing its native side accordingly.
Check out the mvvmcross manifesto:
https://github.com/MvvmCross/MvvmCross/wiki/The-MvvmCross-Manifesto
Mvvmcross on Github:
https://github.com/MvvmCross/MvvmCross
*In this specific example I'll show mvvmcross implementation on Windows 8.1 Store App application.
Those that are familiar with MVC
pattern can find it similar to MVVM
, since its intended to solve the same problem, but in a slightly different way.
While MVC
separates the program to three modules: View, Controller and Model, the MVVM
couples the View and its logic by matching View to appropriate ViewModel
. It makes your software design much more flexible, since the only dependency between the View
and the ViewModel
is established by Binding.
Hardcoded dependency of the view and code logic, (as used in codebehind approach) limits your application dynamics, since your view and code are coupled in a straight way.
For those that are completely new to that concept, I suggest you visit these links:
http://en.wikipedia.org/wiki/Model_View_ViewModel
http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
Once you designed your application as MVVM
, you can simply replace one view with another only by keeping the Binding convention, without touching the logic.
Vice versa, you can switch between ViewModels
of the View without touching the View
.
That is much harder (almost impossible) to achieve while using the codebehind approach.
In our example we are using the MVVM
pattern, but in addition we are separating the logic from the specific application to the PCL library. That will give us the ability to use the same library on different platforms (such as Android, IOS and Windows Phone on Xamarin).
Search for mvvmcross in the "Manage Nugget Packages" window, make sure to install it on both your PCL and you application project.
Or, simply type:
PM> Install-Package MvvmCross.HotTuna.Plugin.All -Version 3.2.2
In your Package Manger Console.
As the nugget finishes its installation, notice that you have two new directories named "ToDo-MvvmCross" in your PCL and the application Project, follow the instructions step by step.
If you done everything right, you can compile and run your application. You should end up with something like this:
This is the main class of your PCL, it derives from MvxApplication and it will be instantiated once, as your library created.
Initialize() method
public override void Initialize()
{
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
RegisterAppStart<ViewModels.FirstViewModel>();
}
This method will iterate on all your classes in the library project and each one that ends up with "Service" convention, will be instantiated as singleton using lazy instantiation in your Mvx
object.
Of course, you could do it manually:
Mvx.RegisterType<TestService>(() => new TestService());
Mvx.RegisterSingleton<ITestservice>(new TestService());
You can change the Initialize method as you please.
For instance:
CreatableTypes()
.EndingWith("Handler")
.AsTypes()
.RegisterAsLazySingleton();
Explanation: Now it will traverse on all classes with "Handler" ending and using lazy instantiation, will register them as Types, not as Interfaces.
Notice as well that we are specifying the ViewModel with which our application will start.
RegisterAppStart<ViewModels.FirstViewModel>();
Dependency injection is a software design pattern in which one or more dependencies are injected, or passed by reference while resolving an object Type.
In mvvmcross the Mvx
class has three ways to register objects:
RegisterType
– Registers the type, by the Interface or by constructor, depending on the overloading function that you choose.
RegisterSingleTone
– Registers the object as singleton (only one instance for the whole application life time).
LazyConstructorAndRegistersingleTon
– Will register the object as single tone, with lazy constructor instantiation. Which means, that only when calling the Resolve()
method of Mvx
class – the object will be instantiated.
In mvvmcross there is a class called Mvx, that uses IOC to register and to resolve dependencies.
You can resolve registered objects only by Resolve()
method, by specifying a type or interface.
It is recommended to use first CanResolve
method or TryResolve
to ensure that you will not get a nasty exception thrown.
After you registered your types to the Mvx
class using the CreateTypes
method or manually, you can now access those classes from any location in your application:
var filehandler = Mvx.Resolve<IFileHandlerAsync>();
For more information see those links:
IOC - http://en.wikipedia.org/wiki/Inversion_of_control
DI - http://en.wikipedia.org/wiki/Dependency_injection
Another component that is added automatically by the mvvmcross nugget is the ViewModels
directory in your library project.
That's where all your ViewModel classes (which derives from MvxViewModel) are stored and will be linked with the Views in your application project by the name convention.
Notice that by default, mvvmcross nugget creates FirstViewModel.cs class in your ViewModels directory, which is linked to FirstView.cs in Views directory in your application project eventually.
The name convention is:
View Convention: [name of the view].cs
ViewModel Convention: [name of the view]Model.cs
When using mvvmcross you will be dealing with Mvx
classes, each ViewModel
class derives from MvxViewModel
, and each View
derives from MvxWindowsPage
.
While sticking to the MVVM
pattern, we are not going to write any codebehind in our View class, so the whole logic of the view will be implemented in the appropriate ViewModel
.
public virtual void Start()
Init()
is the first method that’s called in the ViewModel.
public void Init(IMvxBundle parameters)
Start()
is called after Init()
method, the IMvxBundle
parameter is an object where the passed data from previous ViewModel
are passed as Key-Vaule
pairs, using Dictionary
as data structure.
All the passed data are stored as strings, so in order to use it with a complex object you will need to perform a serialization which can seriously harm your performance.
Usually when dealing with complex object another help class is used for those purposes.
public void Init(IMvxBundle parameters);
protected virtual void InitFromBundle(IMvxBundle parameters);
protected virtual void ReloadFromBundle(IMvxBundle state);
public void ReloadState(IMvxBundle state);
public void SaveState(IMvxBundle state);
protected virtual void SaveStateToBundle(IMvxBundle bundle);
Those method are used for storing and reloading data from the bundle. Usually used when your application went to the background and you are interested in saving some data, that can be lost, since the ViewModel will be reinitialized. The saved data will be read and dealt as the ViewModel reloads, when the application is back to the front.
The call Order:
On ViewModel
Create:
- Constructor ()
- Init()
- InitFromBundle()
- Start()
On ViewModel
Destroy:
SaveStateToBundle()
In our application project we got a new directory called Views, DebugTrsace.cs class and Setup.cs .
Derives from IMvxTrace
interface , which implement 3 methods:
void Trace(MvxTraceLevel level, string tag, Func<string> message);
void Trace(MvxTraceLevel level, string tag, string message);
void Trace(MvxTraceLevel level, string tag, string message, params object[] args);
Using System.Diagnostics.Debug
class for log purposes.
Setup
This is where the application’s setup occur.
These are the methods:
public Setup(Frame rootFrame) : base(rootFrame)
protected override IMvxApplication CreateApp()
protected override IMvxTrace CreateDebugTrace()
Setup
- the constructor of the Setup class.
CreateDebugTrace
– called when creating DebugTrace
, by default – simply returns the DebugTrace
class instance.
CreateApp
– called right before the whole application is initialized and the connections of the mvvmcross components are established. Here you can register to IOC from the application project and use those objects in the PCL with Mvx
class.
For example, if you want to call a DialogMessage
object from your ViewModel in the portable library, you cannot establish it by calling the class directly.Since your project does not have the appropriate Assembly reference for that.
In this case, you can create your class that implement some kind of functionality and register it at the shared Mvx
(IOC) object, right before the CreateApp
method is completed:
protected override IMvxApplication CreateApp()
{
Mvx.RegisterSingleton<IMessageBox>(new MessageBoxHandler());
return new Core.App();
}
Declare the interface in the PCL project so that both projects will be familiar with it.
In the ViewModel
simple call Resolve method of Mvx
class for the desired object instance:
Mvx.Resolve<IMessageBox>().ShowMessage(COMMENT_SUBMIT_SUCCESS);
Now you got your DialogMessage
functionality in your ViewModel.
In your xaml View, you can see that the base xaml node is no longer a Page, it is views:MvxWindowsPage which is a mvvmcross type, the code behind is also deriving from the same class.
Now you can create your view and bind the controls to the ViewModel(which is placed in the PCL).
Mvvmcross is a very convenient platform for developing cross platform applications, it makes your development process much simpler.
Continue reading about mvvmcross plugins, and extensions. There is much more to explore.