{"id":13693189,"url":"https://github.com/aspnetde/MvvmNano","last_synced_at":"2025-05-02T21:31:40.384Z","repository":{"id":65414272,"uuid":"48808621","full_name":"aspnetde/MvvmNano","owner":"aspnetde","description":"The small and smart MVVM framework made with ❤ for Xamarin.Forms.","archived":true,"fork":false,"pushed_at":"2021-04-05T17:49:16.000Z","size":1062,"stargazers_count":59,"open_issues_count":0,"forks_count":9,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-25T04:08:07.960Z","etag":null,"topics":["mvvm","xamarin","xamarin-forms"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aspnetde.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-12-30T16:11:10.000Z","updated_at":"2024-12-16T10:46:00.000Z","dependencies_parsed_at":"2023-01-23T10:55:13.190Z","dependency_job_id":null,"html_url":"https://github.com/aspnetde/MvvmNano","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspnetde%2FMvvmNano","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspnetde%2FMvvmNano/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspnetde%2FMvvmNano/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aspnetde%2FMvvmNano/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aspnetde","download_url":"https://codeload.github.com/aspnetde/MvvmNano/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252108832,"owners_count":21696145,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mvvm","xamarin","xamarin-forms"],"created_at":"2024-08-02T17:01:06.691Z","updated_at":"2025-05-02T21:31:39.902Z","avatar_url":"https://github.com/aspnetde.png","language":"C#","funding_links":[],"categories":["MVVM"],"sub_categories":[],"readme":"# MvvmNano\nThe small and smart MVVM framework made with ❤ for Xamarin.Forms.\n\n| Build Status  | NuGet Package |\n| ------------- | ------------- |\n| [![Build status](https://ci.appveyor.com/api/projects/status/7ts0fqo0vp8fb718?svg=true)](https://ci.appveyor.com/project/ThomasBandt/mvvm-nano)  | [![NuGet version](https://badge.fury.io/nu/MvvmNano.Forms.svg)](https://badge.fury.io/nu/MvvmNano.Forms)  |\n\n1. [Manifesto](#manifesto)\n2. [Download](#download)\n3. [Demo](#demo)\n4. [Getting started](#getting-started)\n5. [Data Binding](#data-binding)\n6. [Navigation](#navigation)\n7. [Dependency Injection](#di)\n8. [Messaging](#messaging)\n9. [Cleaning up](#cu)\n10. [XAML Support](#xaml-support)\n11. [MasterDetailPage](#mdp)\n\n\u003cdiv id='manifesto'/\u003e\n\n## Manifesto\n\n1. Each View (aka Page) must have its own View Model.\n2. Views know their View Models, but not vice versa: View Models never know their Views.\n3. Therefore navigation works from View Model to View Model only, without involving the View.\n4. When navigating, passing complex objects along must be possible.\n5. There should be no limits in how to present Views.\n6. View Models must be easily testable, so Dependency Injection is a basic prerequisite.\n7. Both Views and View Models must be easy to [clean up](https://thomasbandt.com/xamarinios-memory-pitfalls).\n\n\u003cdiv id='download'/\u003e\n\n## Download\n\n    Install-Package MvvmNano.Forms\n\n\u003cdiv id='demo'/\u003e\n\n## Demo\n\nC#: If you are looking for a C# demo, just download this repo and take a look at the demo app which can be found within the /demo folder. Note: It's not using the NuGet packages.\n\nF#: [ixmrm01](https://github.com/ixmrm01) ported the current C# demo to F# and was kind enough to share it. You can take a look at it here: https://github.com/ixmrm01/MvvmNanoDemo\n\n\u003cdiv id='getting-started'/\u003e\n\n## Getting started\n\n### Preliminary remarks\n\n- MvvmNano ships as three .netstandard 2.0 libraries (MvvmNano.Core, MvvmNano.TinyIoC, and MvvmNano.Forms) and also provides three Portable Class Libraries (PCL) with profile 259 (MvvmNano.Core, MvvmNano.Ninject, and MvvmNano.Forms). NuGet will detect which libraries are needed based on your project type.\n- MvvmNano.Forms references [Xamarin.Forms](https://www.nuget.org/packages/Xamarin.Forms/)\n- MvvmNano.Ninject references [Portable.Ninject](https://www.nuget.org/packages/Portable.Ninject/)\n- MvvmNano.Core does not have and external dependency\n\n### Add the NuGet package\n\nYou can add MvvmNano easily via [NuGet](https://www.nuget.org/packages/MvvmNano.Forms/):\n\n    Install-Package MvvmNano.Forms\n\n\u003e **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/.TinyIoC on each target (for example PCL, Xamarin.iOS, Xamarin.Android).\n\nIf you want to use our default IoC container, we provide one for .netstandard2.0 and PCL259.\nThe default container for a PCL project is:\n    \n    Install-Package MvvmNano.Ninject\n    \nFor .netstandard2.0 projects:\n\n    Install-Package MvvmNano.TinyIoC\n\n### Add your first View Model and its Page\n\nYour View Model needs to inherit from `MvvmNanoViewModel\u003cTNavigationParameter\u003e` or `MvvmNanoViewModel`. Let's start with the latter and thereby without a parameter.\n\n```cs\npublic class LoginViewModel : MvvmNanoViewModel\n{\n    // ...\n}\n```\n\nNow 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\u003cTViewModel\u003e`.\n\n```cs\npublic class LoginPage : MvvmNanoContentPage\u003cLoginViewModel\u003e\n{\n    // ...\n}\n```\n\n### Set up your App class\n\nEach Xamarin.Forms app has an entry point – a class called `App` which is derived from `Application`. Change that base class to `MvvmNanoApplication`.\n\nNext 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 in a PCL project, which uses [Portable.Ninject](https://www.nuget.org/packages/Portable.Ninject/), `MvvmNano.TinyIoC` in a .netstandard2.0 project), or go [with your own](http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison).\n\nYou 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).\n\n```cs\npublic class App : MvvmNanoApplication\n{\n    protected override void OnStart()\n    {\n        base.OnStart();\n\n\t\tSetUpMainPage\u003cLoginViewModel\u003e();\n    }\n    \n    protected override IMvvmNanoIoCAdapter GetIoCAdapter()\n\t{\n\t    return new MvvmNanoNinjectAdapter();\n\t}\n}\n```\n\n### That's it!\n\nIf 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.\n\n\u003cdiv id='data-binding'/\u003e\n\n## Data Binding\n\nXamarin.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.\n\n### NotifyPropertyChanged()\n\nMvvmNano View Models implement `INotifyPropertyChanged` and offer a small helper method called `NotifyPropertyChanged()` (without the leading I).\n\n```cs\nprivate string _username;\npublic string Username\n{\n    get { return _username; }\n    set\n    {\n        _username = value;\n        NotifyPropertyChanged();\n        NotifyPropertyChanged(\"IsFormValid\");\n    }\n}\n```\n\nAs 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.\n\n(Scared from so much boilerplate code? Take a look at [Fody PropertyChanged](https://github.com/Fody/PropertyChanged).)\n\n### BindToViewModel()\n\nThis 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:\n\n```cs\nvar nameEntry = new Entry\n{\n    Placeholder = \"Your name\"\n};\n\nBindToViewModel(nameEntry, Entry.TextProperty, x =\u003e x.Username);\n```\n\n### Commands\n\nXamarin.Forms supports `ICommand`, and so does MvvmNano.\n\nView Model:\n\n```cs\npublic MvvmNanoCommand LogInCommand\n{\n\tget { return new MvvmNanoCommand(LogIn); }\n}\n\nprivate void LogIn()\n{\n\t// ...\n}\n```\nPage:\n\n```cs\nBindToViewModel(loginButton, Button.CommandProperty, x =\u003e x.LogInCommand);\n```\n### Commands with parameters\n\nView Model:\n\n```cs\npublic MvvmNanoCommand\u003cstring\u003e LogInCommand\n{\n    get { return new MvvmNanoCommand\u003cstring\u003e(LogIn); }\n}\n\nprivate void LogIn(string userName)\n{\n\t// ...\n}\n```\nPage:\n\n```cs\nBindToViewModel(loginButton, Button.CommandProperty, x =\u003e x.LogInCommand);\nBindToViewModel(loginButton, Button.CommandParameterProperty, x =\u003e x.Username);\n```\n\n\u003cdiv id='navigation'/\u003e\n\n## Navigation\n\nNavigation 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.\n\n\u003e 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.\n\n### Navigation without parameter\n\n```cs\nNavigateTo\u003cAboutViewModel\u003e();\n```\n\nNavigates to `AboutViewModel` without passing a parameter.\n\n### Navigation with a parameter\n\nLet'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\u003cTViewModel\u003e` and make `TViewModel` `Club`.\n\n```cs\npublic class ClubViewModel : MvvmNanoViewModel\u003cClub\u003e\n{\n    public override void Initialize(Club parameter)\n    {\n        // ...\n    }\n}\n```\n\nOverriding the `Initialize()` method will now make that `Club` being passed available after the View Model is being created.\n\nTo actually pass that parameter, navigate to your `ClubViewModel` from the calling View Model as follows:\n\n```cs\nNavigateTo\u003cClubViewModel, Club\u003e(club);\n```\n\n### Opening Pages modally or in a completely customized fashion\n\nThe 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).\n\nA custom presenter could look like this:\n\n```cs\npublic class DemoPresenter : MvvmNanoFormsPresenter\n{\n    public DemoPresenter(Application app) : base(app)\n    {\n    }\n\n    protected override void OpenPage(Page page)\n    {\n        if (page is AboutPage)\n        {\n            Device.BeginInvokeOnMainThread(async () =\u003e\n                await CurrentPage.Navigation.PushModalAsync(new MvvmNanoNavigationPage(page)\n            ));\n\n            return;\n        }\n\n        base.OpenPage(page);\n    }\n}\n```\n\nIn order to pass every navigation request through it, you have register it within your `App` class:\n\n```cs\nprotected override void SetUpPresenter()\n{\n    MvvmNanoIoC.RegisterAsSingleton\u003cIPresenter\u003e(\n        new DemoPresenter(this)\n    );\n}\n```\n\n\u003cdiv id='di'/\u003e\n\n## Dependency Injection\n\nHaving 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.\n\nWe're not inventing the wheel here neither, because the portable version of [Ninject](http://www.ninject.org/) does a fabolous job for us behind the scenes.\n\nIn front of it there is a small static helper class called `MvvmNanoIoC`, which provides the following methods for registering dependencies:\n\n- `MvvmNanoIoC.Register\u003cTInterface, TImplementation\u003e()`\n- `MvvmNanoIoC.RegisterAsSingleton\u003cTInterface, TImplementation\u003e()`\n- `MvvmNanoIoC.RegisterAsSingleton\u003cTInterface\u003e(TInterface instance)`\n- `MvvmNanoIoC.Resolve\u003cTInterface\u003e()`\n\n### Sample: Registering a dependency\n\n```cs\npublic class App : MvvmNanoApplication\n{\n    protected override void OnStart()\n    {\n        base.OnStart();\n\n        SetUpDependencies();\n    }\n\n    private static void SetUpDependencies()\n    {\n        MvvmNanoIoC.Register\u003cIClubRepository, MockClubRepository\u003e();\n    }\n}\n```\n\n### Sample: Constructor Injection\n\n```cs\npublic class WelcomeViewModel : MvvmNanoViewModel\n{\n    public List\u003cClub\u003e Clubs { get; private set; }\n\n    public WelcomeViewModel(IClubRepository clubs)\n    {\n        Clubs = clubs.All();\n    }\n}\n```\n\nPS: Usually you won't need the `Resolve\u003cTInterface\u003e()` method, because constructor injection works out of the box.\n\n### Using another IoC Container than Ninject\n\nIf you want to use another IoC Container, just implement `IMvvmNanoIoCAdapter` and return an instance of this implementation in your App's class `GetIoCAdapter()` method.\n\n\u003cdiv id='messaging'/\u003e\n\n## Messaging\n\nThis is very opinionated and certainly optional, but the official interface for messaging within Xamarin.Forms seems a bit odd. See more about it [here](https://thomasbandt.com/a-nicer-messaging-interface-for-xamarinforms).\n\nThe solution of `IMessenger` presented in that blog post comes with MvvmNano and is automatically registered in `MvvmNanoApplication`.\n\n### Inject IPresenter to your View Model\n\n```cs\npublic class MyViewModel : MvvmNanoViewModel\n{\n    private readonly IMessenger _messenger;\n\n    public WelcomeViewModel(IMessenger messenger)\n    {\n        _messenger = messenger;\n    }\n}\n```\n\n### Define your message\n\n```cs\npublic class AlbumCreatedMessage : IMessage\n{\n    public readonly Album Album;\n\n    public AlbumCreatedMessage(Album album)\n    {\n        Album = album;\n    }\n}\n```\n\n### Send it around\n\n```cs\nvar album = new Album\n{\n    Id = Guid.NewGuid(),\n    Title = \"Hello World\"\n};\n\n_messenger.Send(new AlbumCreatedMessage(album));\n```\n\n### Subscribe to it\n\n```cs\n_messenger.Subscribe\u003cAlbumCreatedMessage\u003e(this, AlbumAdded);\n\nprivate void AlbumAdded(object sender, AlbumCreatedMessage message)\n{\n    // Do something\n}\n```\n\n### When you're done, unsubscribe\n\n```cs\n_messenger.Unsubscribe\u003cAlbumCreatedMessage\u003e(this);\n```\n\n\u003cdiv id='cu'/\u003e\n\n## Cleaning up\n\nCleaning up your View Models _and_ your Views aka Pages is a must in order to prevent memory leaks. Read more about it [here](https://thomasbandt.com/xamarinios-memory-pitfalls). Unfortunately Xamarin doesn' think that way, so their whole Xamarin.Forms framework lacks `IDisposable` implementations.\n\nMvvmNano 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.\n\n\u003e **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.\n\n\u003cdiv id='xaml-support'/\u003e\n\n## XAML Support\n\nXAML is fully supported, take a look at the demo or these snippets.\n\nView Model:\n\n```cs\npublic class ClubViewModel : MvvmNanoViewModel\u003cClub\u003e\n{\n    private string _name;\n    public string Name\n    {\n        get { return _name; }\n        private set { _name = value; NotifyPropertyChanged(); }\n    }\n\n    private string _country;\n    public string Country\n    {\n        get { return _country; }\n        private set { _country = value; NotifyPropertyChanged(); }\n    }\n\n    public override void Initialize(Club parameter)\n    {\n        Name = parameter.Name;\n        Country = parameter.Country;\n    }\n}\n```\n\nPage:\n\n```xaml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cpages:MvvmNanoContentPage xmlns=\"http://xamarin.com/schemas/2014/forms\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\n    xmlns:pages=\"clr-namespace:MvvmNano.Forms\"\n    xmlns:vm=\"clr-namespace:MvvmNanoDemo\"\n    x:Class=\"MvvmNanoDemo.ClubPage\"\n    x:TypeArguments=\"vm:ClubViewModel\"\n    Title=\"{Binding Name}\"\u003e\n    \u003cContentPage.Content\u003e\n        \u003cStackLayout\u003e\n            \u003cLabel Text=\"{Binding Country}\" /\u003e\n        \u003c/StackLayout\u003e\n    \u003c/ContentPage.Content\u003e\n\u003c/pages:MvvmNanoContentPage\u003e\n```\n\n\u003cdiv id='mdp'/\u003e\n\n## Master Detail Page\n\nMaster detail pages are supported in MvvmNano, its a page type that contains a fly out menu (master) and page area (detail).\nThe master is typically populated with a listview or something similiar to chose the detail from.\n\nCreate a page that inherits from the `MvvmNanoDefaultMasterDetailPage` and a corresponding view model.\nYou can add details with the `AddDetailData\u003cTViewModel\u003e(MvvmNanoMasterDetailData data)` method available in your master detail page.\nInformation about each detail are stored in a `MvvMNanoMasterDetailData`, which you can inherit from  and add additional properties to.\nYou can use these properties to present additional informations in the master area.\n\nThe `MvvmNanoDefaultMasterDetailPage` is using a `ListView` to display all available details. You can override `DataTemplate GetItemTemplate()` to create your own DataTemplate or override the `Page CreateMasterPage()` to add a header, footer or embed the `ListView` in a view. An example for that can be found in the Demo.Pages.MasterPage.cs.\n \n### Create your own master detail layout\n\nIf you decide to create your complete own layout however, the `MvvmNanoMasterDetailPage` provides multiple methods to hook up your layout. Use the `MvvmNanoMasterDetailPage` to inherit from, instead of the `MvvmNanoDefaultMasterDetailPage` in this case.\n\nOverride `Page CreateMasterPage()` and return your own layout that will be used in the master area.\nOverride `void DetailDataAdded\u003cTViewModel\u003e(MvvmNanoMasterDetailData detailData)` to execute some logic when a new detail data item is added. All added items are available in the `ObservableCollection\u003cMvvmNanoMasterDetailData\u003e MasterDetails`.\n\nYou can call `void SetDetail(Type viewModelType)` or `void SetDetail(MvvmNanoMasterDetailData data)` to present a new page in the detail area. MvvmNano will take care of disposing the old page, creating the new view and viewmodel and hook up the binding context. \nAfter a new detail is set, `void DetailSet(MvvmNanoMasterDetailData lastDetailData, MvvmNanoMasterDetailData newDetailData, Page page)` is called. You can override that aswell, for example to highlight the current detail in the master area.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faspnetde%2FMvvmNano","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faspnetde%2FMvvmNano","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faspnetde%2FMvvmNano/lists"}