The small and smart MVVM framework made with ❤ for Xamarin.Forms.
Build Status | NuGet Package |
---|---|
- Manifesto
- Download
- Demo
- Getting started
- Data Binding
- Navigation
- Dependency Injection
- Cleaning up
- XAML Support
- Each View (aka Page) must have its own View Model.
- Views know their View Models, but not vice versa: View Models never know their Views.
- Therefore navigation works from View Model to View Model only, without involving the View.
- When navigating, passing complex objects along must be possible.
- There should be no limits in how to present Views.
- View Models must be easily testable, so Dependency Injection is a basic prerequisite.
- Both Views and View Models must be easy to clean up.
Install-Package MvvmNano.Forms
Just download this repo and take a look at the demo app which can be found within the /demo folder.
- MvvmNano comes as two Portable Class Libraries (PCL) with profile 78 (MvvmNano.Core and MvvmNano.Forms)
- MvvmNano.Forms references Xamarin.Forms
- MvvmNano.Core references Portable.Ninject
You can add MvvmNano easily via NuGet:
Install-Package MvvmNano.Forms
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).
Your View Model needs to inherit from MvvmNanoViewModel<TNavigationParameter>
or MvvmNanoViewModel
. Let's start with the latter and thereby without a parameter.
public class LoginViewModel : MvvmNanoViewModel
{
// ...
}
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>
.
public class LoginPage : MvvmNanoContentPage<LoginViewModel>
{
// ...
}
Each Xamarin.Forms app has an entry point – a class called App
which is derived from Application
. Change that base class to MvvmNanoApplication
.
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).
public class App : MvvmNanoApplication
{
protected override void OnStart()
{
base.OnStart();
SetUpMainPage();
}
private void SetUpMainPage()
{
var viewModel = MvvmNanoIoC.Resolve<LoginViewModel>();
viewModel.Initialize();
var page = new LoginPage();
page.SetViewModel(viewModel);
MainPage = new MvvmNanoNavigationPage(page);
}
}
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.
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.
MvvmNano View Models implement INotifyPropertyChanged
and offer a small helper method called NotifyPropertyChanged()
(without the leading I).
private string _username;
public string Username
{
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.
(Scared from so much boilerplate code? Take a look at Fody PropertyChanged.)
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:
var nameEntry = new Entry
{
Placeholder = "Your name"
};
BindToViewModel(nameEntry, Entry.TextProperty, x => x.Username);
Xamarin.Forms supports ICommand
, and so does MvvmNano.
View Model:
public MvvmNanoCommand LogInCommand
{
get { return new MvvmNanoCommand(LogIn); }
}
private void LogIn()
{
// ...
}
Page:
BindToViewModel(loginButton, Button.CommandProperty, x => x.LogInCommand);
View Model:
public MvvmNanoCommand<string> LogInCommand
{
get { return new MvvmNanoCommand<string>(LogIn); }
}
private void LogIn(string userName)
{
// ...
}
Page:
BindToViewModel(loginButton, Button.CommandProperty, x => x.LogInCommand);
BindToViewModel(loginButton, Button.CommandParameterProperty, x => x.Username);