Important: Add it to your Xamarin.Forms library as well as to your native app projects, so NuGet can resolve the right assemblies of the dependencies Xamarin.Forms and Portable.Ninject on each target (for example PCL, Xamarin.iOS, Xamarin.Android).
If you want to use our default IoC container (Ninject Portable), also add MvvmNano.Ninject:
Install-Package MvvmNano.Ninject
Add your first View Model and its Page
Your View Model needs to inherit from MvvmNanoViewModel<TNavigationParameter> or MvvmNanoViewModel. Let's start with the latter and thereby without a parameter.
Now add the Page. Note that by convention it needs to be named after your View Model, except for the ViewModel suffix (so LoginViewModel becomes LoginPage). You also need to inherit from MvvmNanoContentPage<TViewModel>.
Each Xamarin.Forms app has an entry point – a class called App which is derived from Application. Change that base class to MvvmNanoApplication.
Next you are asked to implement the method GetIoCAdapter() which is expected to return an implementation of IMvvmNanoIoCAdapter. Just go with our default choice (MvvmNano.Ninject, which uses Portable.Ninject), or go with your own.
You also want to tell your application the first Page and View Model which should be used when the app gets started for the first time. Put this setup inside of OnStart(), but don't forget to call base.OnStart(). This is important in order to set up the Presenter correctly (for more on that see below).
If you now build and run your app(s), you'll see your first Page which is running with it's View Model behind. Nothing spectacular so far, but the fun is just getting started.
## Data Binding
Xamarin.Forms comes with really powerful data binding features which you're fully able to leverage with MvvmNano, so we are not reinventing the wheel here.
NotifyPropertyChanged()
MvvmNano View Models implement INotifyPropertyChanged and offer a small helper method called NotifyPropertyChanged() (without the leading I).
privatestring_username;publicstringUsername{
get {return_username;}
set
{_username=value;
NotifyPropertyChanged();
NotifyPropertyChanged("IsFormValid");}}
As you can see, NotifyPropertyChanged() can be called with and without the name of the property it should be notifying about. If you leave it out, it will automatically use the name of the property you're calling it from.
This is a small helper method baked in to MvvmNanoContentPage, which makes binding to your View Model a no-brainer when writing your views (pages) in code:
Navigation works from View Model to View Model only, not involving the View aka Page directly. Instead all work is delegated to a central Presenter, which is responsible for creating the Page, its View Model and also passing a parameter, if specified.
This way you can keep your application independent from the UI implementation – if you ever have to switch to Xamarin.iOS or Xamarin.Android, in parts or even completely, you don't have to throw your View Models away.
Navigation without parameter
NavigateTo<AboutViewModel>();
Navigates to AboutViewModel without passing a parameter.
Navigation with a parameter
Let's say you want to get a parameter of the type Club each time your View Model is being called. Then you have to derive from MvvmNanoViewModel<TViewModel> and make TViewModelClub.
Overriding the Initialize() method will now make that Club being passed available after the View Model is being created.
To actually pass that parameter, navigate to your ClubViewModel from the calling View Model as follows:
NavigateTo<ClubViewModel,Club>(club);
Opening Pages modally or in a completely customized fashion
The default presenter coming with MvvmNano will push a page to the existing navigation stack. But you are completely free to customize that, so you can define on a per-View Model basis how its view should be presented (maybe displayed modally or rendered in a completely different way).
A custom presenter could look like this:
publicclassDemoPresenter:MvvmNanoFormsPresenter{publicDemoPresenter(Applicationapp):base(app){}protectedoverridevoidOpenPage(Pagepage){if(page is AboutPage){
Device.BeginInvokeOnMainThread(async()=>await CurrentPage.Navigation.PushModalAsync(new MvvmNanoNavigationPage(page)));return;}base.OpenPage(page);}}
In order to pass every navigation request through it, you have register it within your App class:
Having a Initialize() or Initialize(TNavigationParameter parameter) method in your View Model comes with a benefit: the constructor is still free for parameters being automatically injected.
We're not inventing the wheel here neither, because the portable version of Ninject does a fabolous job for us behind the scenes.
In front of it there is a small static helper class called MvvmNanoIoC, which provides the following methods for registering dependencies:
PS: Usually you won't need the Resolve<TInterface>() method, because constructor injection works out of the box.
Using another IoC Container than Ninject
If you want to use another IoC Container, just implement IMvvmNanoIoCAdapter and return an instance of this implementation in your App's class GetIoCAdapter() method.
## Messaging
This is very opinionated and certainly optional, but the official interface for messaging within Xamarin.Forms seems a bit odd. See more about it here.
The solution of IMessenger presented in that blog post comes with MvvmNano and is automatically registered in MvvmNanoApplication.
Cleaning up your View Models and your Views aka Pages is a must in order to prevent memory leaks. Read more about it here. Unfortunately Xamarin doesn' think that way, so their whole Xamarin.Forms framework lacks IDisposable implementations.
MvvmNano fixes that. Both MvvmNanoViewModel and MvvmNanoContentPage implement IDisposable, so you can use the Dispose() method in both to detach event handlers, dispose "heavy resources" such as images etc.
Important: In order to get that Dispose() method actually called, you must use MvvmNanoNavigationPage instead of the framework's default Navigationpage. It takes care of calling Dispose() at the right time whenever a Page is being removed from the stack.
## XAML Support
XAML is fully supported, take a look at the demo or these snippets.