FreshMvvm – A Mvvm Framework designed for Xamarin.Forms

Now available via Nuget FreshMvvm is a Mvvm framework that’s designed specifically for Xamarin.Forms. The reason I say it’s designed for Xamarin.Forms is because it plays on Xamarin.Forms strengths and fills in ONLY the missing parts. It has a requirement for Xamarin.Forms and therefore is smart and can do thing such as wiring up the BindingContext and Page events.

Some of the feature for FreshMvvm include:

  • PageModel to PageModel Navigation

  • Automatic wiring of BindingContext

  • Automatic wiring of Page events (eg appearing)

  • Basic methods on PageModel (init, reverseinit)

  • Built in IOC Container

  • PageModel Constructor Injection

  • Basic methods available in Model, like Alert

  • Built in Navigation containers for SimpleNavigation, Tabbed and MasterDetail

How does it compare to other Mvvm options?

  • It’s super light and super simple

  • It’s specifically designed for Xamarin.Forms, nothing else does this currently

  • Designed to be easy to learn and develop (great when your not ready for RxUI)

  • Uses a Convention over Configuration

The Story

I was part-way into a Xamarin Traditional application when Xamarin.Forms was released. I wanted to move the project onto Xamarin.Forms but on that project I was using MvvmCross. At that time MvvmCross had no support for Xamarin.Forms, so I had the option of 1) adapting MvvmCross, 2) finding an alternative or 3) rolling my own Mvvm. The best part about MvvmCross was it’s two-way databinding to the native iOS/Android controls but since Xamarin.Forms already had the Databinding builtin, that wasn’t useful and the size with MvvmCross was an overhead when I didn’t require it. I also wasn’t able to find an alternative that I could easily move to. So that I could keep it simple and flexible, I ended up rolling my own Mvvm.

It’s grown up from this post on rolling your own Mvvm for Xamarin.Forms. I try hard to keep the simplicity of rolling your own Mvvm for Xamarin.Forms.

It was never a plan to create a framework but after presenting my Mvvm solution at a few events, I found many people wanted it and seemed to be really interested in it. Also considering I’ve been using this Framework in all my projects from the start of Xamarin.Forms I know that it works, so I created FreshMvvm and that’s how it was born.

Conventions

This Framework, while simple, is also powerful and uses a Convention over Configuration style.

Note Different to standard naming conventions, FreshMvvm uses Page and PageModel instead of View and ViewModel, this is inline with Xamarin.Forms using Pages

  • A Page must have a corresponding PageModel, with naming important so a QuotePageModel must have a QuotePage The BindingContext on the page will be automatically set with the Model

  • A PageModel can have a Init method that takes a object

  • A PageModel can have a ReverseInit method that also take a object and is called when a model is poped with a object

  • PageModel can have dependancies automatically injected into the Constructor

Navigation

The Primary form of Navigation in FreshMvvm is PageModel to PageModel, this essentially means our views have no idea of Navigation.

So to Navigate between PageModels use:

1
await CoreMethods.PushPageModel<QuotePageModel>(); // Pushes on navigation stack
1
await CoreMethods.PushPageModel<QuotePageModel>(null, true); // Pushes a Modal

The engine for Navigation in FreshMvvm is done via a simple interface, with methods for Push and Pop. Essentially these methods can control the Navigation of the application in any way they like.

1
2
3
4
5
public interface IFreshNavigationService
{
   Task PushPage(Page page, FreshBasePageModel model, bool modal = false);
   Task PopPage(bool modal = false);
}

This is what you as a consumer will need to implement, and then register in the IOC Container.

Within the PushPage and PopPage you can do any type of navigation that you like, this can from a simple navigation to a advanced nested navigation.

The Framework contains some built in Navigation containers for the different types of Navigation.

Basic Navigation – Built In

1
2
3
4
5
var page = FreshBasePageModel.ResolvePageModel<MainMenuPageModel> ();
 
var basicNavContainer = new FreshNavigationContainer (page);
 
MainPage = basicNavContainer;

Master Detail – Built In

1
2
3
4
5
6
7
8
9
var masterDetailNav = new FreshMasterDetailNavigationContainer ();
 
masterDetailNav.Init ("Menu");
 
masterDetailNav.AddPage<ContactListPageModel> ("Contacts", null);
 
masterDetailNav.AddPage<QuoteListPageModel> ("Pages", null);
 
MainPage = masterDetailNav;

Tabbed Navigation – Built In

1
2
3
4
5
6
7
var tabbedNavigation = new FreshTabbedNavigationContainer ();
 
tabbedNavigation.AddTab<ContactListPageModel> ("Contacts", null);
 
tabbedNavigation.AddTab<QuoteListPageModel> ("Pages", null);
 
MainPage = tabbedNavigation;

Implementing Custom Navigation

It’s possible to setup any type of Navigation by implementing IFreshNavigationService.There’s a sample of this in Sample Application named CustomImplementedNav.cs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/// <summary>
/// This is a sample custom implemented Navigation. It combines a MasterDetail and a TabbedPage.
/// </summary>
public class CustomImplementedNav : Xamarin.Forms.MasterDetailPage, IFreshNavigationService
{
    FreshTabbedNavigationContainer _tabbedNavigationPage;
    Page _contactsPage, _quotesPage;
 
    public CustomImplementedNav ()
    {   
        SetupTabbedPage ();
        CreateMenuPage ("Menu");
        RegisterNavigation ();
    }
 
    void SetupTabbedPage()
    {
        _tabbedNavigationPage = new FreshTabbedNavigationContainer ();
        _contactsPage = _tabbedNavigationPage.AddTab<ContactListPageModel> ("Contacts", null);
        _quotesPage = _tabbedNavigationPage.AddTab<QuoteListPageModel> ("Quotes", null);
        this.Detail = _tabbedNavigationPage;
    }
 
    protected void RegisterNavigation()
    {
        FreshIOC.Container.Register<IFreshNavigationService> (this);
    }
 
    protected void CreateMenuPage(string menuPageTitle)
    {
        var _menuPage = new ContentPage ();
        _menuPage.Title = menuPageTitle;
        var listView = new ListView();
 
        listView.ItemsSource = new string[] { "Contacts", "Quotes", "Modal Demo" };
 
        listView.ItemSelected += async (sender, args) =>
        {
 
            switch ((string)args.SelectedItem) {
            case "Contacts":
                _tabbedNavigationPage.CurrentPage = _contactsPage;
                break;
            case "Quotes":
                _tabbedNavigationPage.CurrentPage = _quotesPage;
                break;
            case "Modal Demo":
                var modalPage = FreshPageModelResolver.ResolvePageModel<ModalPageModel>();
                await PushPage(modalPage, null, true);
                break;
            default:
            break;
            }
 
            IsPresented = false;
        };
 
        _menuPage.Content = listView;
 
        Master = new NavigationPage(_menuPage) { Title = "Menu" };
    }
 
    public virtual async Task PushPage (Xamarin.Forms.Page page, FreshBasePageModel model, bool modal = false)
    {
        if (modal)
            await Navigation.PushModalAsync (new NavigationPage(page));
        else
            await ((NavigationPage)_tabbedNavigationPage.CurrentPage).PushAsync (page); 
    }
 
    public virtual async Task PopPage (bool modal = false)
    {
        if (modal)
            await Navigation.PopModalAsync ();
        else
            await ((NavigationPage)_tabbedNavigationPage.CurrentPage).PopAsync (); 
    }
}

Inversion of Control (IOC)

So that you don’t need to include your own IOC container, FreshMvvm comes with a IOC container built in. It’s using TinyIOC underneith, but with different naming to avoid conflicts.

To Register services in the container use Register:

1
FreshIOC.Container.Register<IDatabaseService, DatabaseService> ();

To obtain a service use Resolve:

1
FreshIOC.Container.Resolve<IDatabaseService> ();

*This is also what drives constructor injection.

PageModel – Constructor Injection

When PageModels are pushed services that are in the IOC container can be pushed into the Constructor.

1
2
3
4
5
6
7
8
public class ContactListPageModel : FreshBasePageModel
{
    IDatabaseService _databaseService;
 
    public ContactListPageModel (IDatabaseService databaseService)
    {
        _databaseService = databaseService;
    }

PageModel Important Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/// <summary>
/// The previous page model, that's automatically filled, on push
/// </summary>
public FreshBasePageModel PreviousPageModel { get; set; }
 
/// <summary>
/// A reference to the current page, that's automatically filled, on push
/// </summary>
public Page CurrentPage { get; set; }
/// <summary>
/// Core methods are basic built in methods for the App including Pushing, Pop and Alert
/// </summary>
public IPageModelCoreMethods CoreMethods { get; set; }
 
/// <summary>
/// This method is called when a page is Pop'd, it also allows for data to be returned.
/// </summary>
/// <param name="returndData">This data that's returned from </param>
public virtual void ReverseInit(object returndData) { }
 
/// <summary>
/// This method is called when the PageModel is loaded, the initData is the data that's sent from pagemodel before
 
/// </summary>
/// <param name="initData">Data that's sent to this PageModel from the pusher</param>
public virtual void Init(object initData) { }
 
/// <summary>
/// This method is called when the view is disappearing.
/// </summary>
protected virtual void ViewIsDisappearing (object sender, EventArgs e)
{
 
}
 
/// <summary>
/// This methods is called when the View is appearing
/// </summary>
protected virtual void ViewIsAppearing (object sender, EventArgs e)
{
}

The CoreMethods

Each PageModel has a property called ‘CoreMethods’ which is automatically filled when a PageModel is pushed, it’s the basic functions that most apps need like Alerts, Pushing, Poping etc.

1
2
3
4
5
6
7
8
9
10
public interface IPageModelCoreMethods
{
   Task DisplayAlert (string title, string message, string cancel);
   Task<string> DisplayActionSheet (string title, string cancel, string destruction, params string[] buttons);
   Task<bool> DisplayAlert (string title, string message, string accept, string cancel);
   Task PushPageModel<T>(object data, bool modal = false) where T : FreshBasePageModel;
   Task PopPageModel(bool modal = false);
   Task PopPageModel(object data, bool modal = false);
   Task PushPageModel<T>() where T : FreshBasePageModel;
}

 

Sample PageModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
[ImplementPropertyChanged] // Use Fody for Property Changed Notifications
public class QuoteListPageModel : FreshBasePageModel
{
   IDatabaseService _databaseService;
 
   //These are automatically filled via Constructor Injection IOC
   public QuoteListPageModel (IDatabaseService databaseService)
   {
       _databaseService = databaseService;
   }
 
   public ObservableCollection<Quote> Quotes { get; set; }
 
   public override void Init (object initData)
   {
       Quotes = new ObservableCollection<Quote> (_databaseService.GetQuotes ());
   }
 
   //The Framework support standard functions list appeaing and disappearing
   protected override void ViewIsAppearing (object sender, System.EventArgs e)
   {
       CoreMethods.DisplayAlert ("Page is appearing", "", "Ok");
       base.ViewIsAppearing (sender, e);
   }
 
   protected override void ViewIsDisappearing (object sender, System.EventArgs e)
   {
       base.ViewIsDisappearing (sender, e);
   }
 
   //This is called when a pushed Page returns to this Page
   public override void ReverseInit (object value)
   {
       var newContact = value as Quote;
 
       if (!Quotes.Contains (newContact)) {
           Quotes.Add (newContact);
       }
   }
 
   public Command AddQuote {
       get {
           return new Command (async () => {
               //Push A Page Model
 
               await CoreMethods.PushPageModel<QuotePageModel> ();
           });
       }
   }
 
   Quote _selectedQuote;
   public Quote SelectedQuote {
       get {
           return _selectedQuote;
       }
       set {
           _selectedQuote = value;
           if (value != null)
               QuoteSelected.Execute (value);
       }
   }
 
   public Command<Quote> QuoteSelected {
       get {
           return new Command<Quote> (async (quote) => {
               await CoreMethods.PushPageModel<QuotePageModel> (quote);
           });
       }
   }
}
 
So please take a look at it on <a href="https://github.com/rid00z/FreshMvvm">github</a> or nuget. 
 
If you have any questions please contact me via <a href="http://www.michaelridland.com">http://www.michaelridland.com</a>. 
 
Thanks
 
Michael

10 Responses

  1. Hi Michael. Looks like a great framework. Do you have a small sample project available that demonstrates the framework?

  2. Hi Michael,

    Does FreshMVVM duplicate the functionality of Xamarin.Forms MVVM and Dependency Service? If not, when to use one or the other?

    Thanks for your freat work and more power.

    Regards,

    Jose

    1. Hi Jose

      FreshMvvm doesn’t duplicate any Xamarin.Forms functionality, it only fills in the missing pieces of Mvvm for Xamarin.Forms and puts it into a Framework.

      Thanks.

  3. How can I open a FreshTabbedNavigationContainer from a menu item of a FreshMasterDetailNavigationContainer?

    1. Hi, in this case you wouldn’t use a FreshTabbedNavigationContainer, just a normal Xamarin.Forms.TabbedPage.

      If you want it all to work inside the PageModelPushing you’ll need to implement a custom navigation container.

      That container can have both the Navigation Pages.

  4. Michael,
    Any plans to support UWP? I tried to add it to a new app I\\\\\\\’m working on and I get the following error:
    “FreshMvvm 0.0.6 is not compatible with UAP,Version=v10.0 (win10-x64-aot).”

    Thanks,
    Philip

    1. Yes it will definitely work with UWP. I’m actually not sure why this isn’t working. I will need to look into it.

Leave a Reply