Best practice MVVM navigation using Master Detail page?

FranciscoGGFranciscoGG ✭✭ESMember ✭✭
edited August 2015 in Xamarin.Forms

Hi to all.

I want to follow the MVVM pattern as much as possible, but I don't know if I am doing the navigation quite well. Note that I am using a MasterDetail page and I want to maintain the Master page, changing only the Detail side when I navigate.

Here is the way I navigate from my ViewModel. In this example, from ViewModelOne to ViewModelTwo:

public class ViewModelOne : ViewModelBase
{
    private void GoToViewTwo()
    {
        var viewTwo = new ViewTwo(new ViewModelTwo());
        ((MasterView)Application.Current.MainPage).NavigateToPage(viewTwo);
    }
}

MasterView implementation:

public class MasterView : MasterDetailPage
{
    public void NavigateToPage(Page page)
    {
        Detail = new NavigationPage(page);
        IsPresented = false;
    }
}

ViewTwo implementation:

public partial class ViewTwo : PageBase
{
    public MenuView(ViewModelTwo vm)
        : base(vm)
    {
        InitializeComponent();
    }
}

PageBase implementation:

public class PageBase : ContentPage
{
    public PageBase(ViewModelBase vmb)
    {
        this.BindingContext = vmb;
    }
}

Is this the best approach (and best performance) for do the navigation? When I do some navigations, the app starts to run slower and maybe there is something I am not doing fine.
Is this the best approach for do the navigation showing always a MasterDetail page?

Thanks.

Posts

  • FranciscoGGFranciscoGG ✭✭ ESMember ✭✭

    ...please give me some light :)

  • FranciscoGGFranciscoGG ✭✭ ESMember ✭✭

    Please, somebody can help me? I don't have clear how to do...

  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭

    @FranciscoGG - You are right in that you do the navigation in your ViewModel but you aren't navigating correctly between pages.

    You are creating a new NavigationPage each time. Create 1 NavigationPage and use PushAsync. E.g.

    NavigationPage _navPage = new NavigationPage(new DefaultPage());
    
    // Assign your NavigationPage to your MasterDetail
    Detail = _navPage;
    
    // When you want to move
    _navPage.PushAsync(new MyNewPage());
    

    Then pressing the back button will go back to the first page. You can also go

    _navPage.PopAsync();

    To move back one page.

  • FranciscoGGFranciscoGG ✭✭ ESMember ✭✭
    edited August 2015

    @AdamP, the problem is that I don't want to do PushAsync, because I want to keep the Master page in the left side, instead the back button, but when I do:

    _navPage.PushAsync(new MyNewPage());
    

    I am missing the Master page :(

    I want to navigate only changing the Detail page...Thanks :) :)

  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭
    edited August 2015

    @FrancisoGG - That is why you put the NavigationPage inside the detail page. When you do that the Master page is still there, even when you go back and forth as you have:

    Master
       - Detail
           - NavigationPage
                 - Page1
                 - Page2
    
  • FranciscoGGFranciscoGG ✭✭ ESMember ✭✭

    Ok @AdamP, it has sense :smile:

    But I have two questions...

    1.- Where do you do the Navigation? You create a service for do the Navigation? Or do you call PushAsync allways from MasterPage?

    2.- I see you don't instance the ViewModel... How do you do this? Because I do the instance of the ViewModel and then I pass as parameter to the view constructor (as you can see in my 1st message)

    Thanks!

  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭

    @FranciscoGG - also I started this a yesterday, it is not finished yet but may help you out.

    https://github.com/adamped/xarch-starter

  • FranciscoGGFranciscoGG ✭✭ ESMember ✭✭
    edited August 2015

    @AdamP I don't understand your approach of the navigation...Why do you pass the navigation page by parameter? There is already a NavigationService by default by Xamarin... (INavigation)

    Anyways, which MVVM framework for Xamarin Forms do you recommend me?

  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭

    @FranciscoGG - I personally use MVVMLight and a few others around here do. However I think MVVMCross seems to have more community around the Xamarin Forums, so you might get more help with that framework than with MVVMLight. But that's just my speculation, I haven't run a poll or anything.

  • NMackayNMackay mod GBInsider, University mod

    @FranciscoGG
    I can send you an MVVM Light sample to get you started if it helps.

    @AdamP

    I have started using a different navigation interface to make handing Modal pages easier. If you want I can PM you the implementation, it's a little rough but seems to work. Would be useful to get your thoughts,

    public interface INavService
        {
            string CurrentPageKey { get; }
            string CurrentModalPageKey { get; }
            int ModalStackCount { get; }
            Task GoBack();
            Task Home();
            Task PopModal();
            Task PushModal(string pageKey);
            Task NavigateTo(string pageKey);
            Task NavigateTo(string pageKey, object parameter);
        }
    
  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭

    @NMackay - I rolled my own Nav service as well. I actually started a github project just for getting a simple project starter template happening. Only started yesterday so I haven't finished it yet but here is specifically my NavigationService

    https://github.com/adamped/xarch-starter/blob/master/Mobile/Helper/ExtNavigationService.cs

    I haven't completed that fully because in my other project I did Modal methods similar to what you have done above. I just haven't built them in yet.

    However happy to chat about it, even collaborate with the project on github.

    The easiest way would be using Slack. Are you on the Xamarin Slack channel? You can get an invite here:

    https://xamarinchat.herokuapp.com/

    Then download Slack https://slack.com/apps

    Not sure if you have used it before, but I love it. I am always on it because the company I am currently contracted to uses it, and I use it personally for my own company as well. Plus now Xamarin Chat.

    We can start a private group or chat in there. Much easier to communicate on.

  • FranciscoGGFranciscoGG ✭✭ ESMember ✭✭
    edited August 2015

    @NMackay , thanks.

    But, if you do the navigation with parameters, how do they reach to the ViewModel of the page to navigate?

  • NMackayNMackay mod GBInsider, University mod

    @FranciscoGG

    I just pass it to the page constructor and then execute an async relayCommand to do the data fetch in the VM of the page so nothing gets blocked.

    namespace VesselFinder.View
    {
        public partial class PerformancePage : IPageLifetime
        {
            public PerformancePage(Vessel vessel)
            {
                InitializeComponent();
                ((PerformanceViewModel)this.BindingContext).RefreshCommand.Execute(vessel);
            }
    
            public void CleanupPage()
            {
                SimpleIoc.Default.Unregister<PerformanceViewModel>();
            }
        }
    }
    
  • Martijn00Martijn00 ✭✭✭ NLInsider, University ✭✭✭

    @FranciscoGG There are some great examples available for Xamarin.Forms with MvvmCross: https://github.com/MvvmCross/MvvmCross-Forms/tree/master/Samples

    There is also a lot of information and documentation in the wiki: https://github.com/MvvmCross/MvvmCross-Forms/wiki

  • Pierre-ChristopheDusPierre-ChristopheDus ✭✭✭ FRUniversity ✭✭✭

    Hi guys, I am facing a similar case for a project and I'm not sure to having the good way for solving it...
    A MasterDetailPage must be the the root page of my app, and will contain some pages:

    • a "Home page": a limited list of stores and its associated offers
    • a "Stores list page": the full list of stores
    • a "Offers list page": the full list of offers
    • other simple pages: contact page, informations, ...

    From these pages, I could navigate to the "detail" pages for an Offer or a Store. I must also be able to "navigate" between the Offer page and the Store page.

    I based on the example Hanselman.Forms from James Montemagno to implement the refresh of the Detail page:
    https://github.com/jamesmontemagno/Hanselman.Forms

    So the MasterDetailPage has a dictionnary that contains the pages and the associated menu item that will be linked to the Detail page:

    public RootPage()
    {
        Pages = new Dictionary<MenuType, NavigationPage>();
        Master = new MenuPage(this);
    
        BindingContext = new BaseViewModel
        {
            Title = "Test",
            Icon = "hamburger.png"
        };
        //setup home page
        NavigateAsync(MenuType.Item1);
    
        InvalidateMeasure();
    }
    

    The NavigateAsync() method creates a customized NavigationPage for each page of the MasterDetailPage:

    public async Task NavigateAsync(MenuType id)
    {
        Page newPage;
        if (!Pages.ContainsKey(id))
        {
            switch (id)
            {
                case MenuType.Item1:
                    Pages.Add(id, new DNNavigationPage(new LoginPage(id)));
                    break;
                case MenuType.Item2:
                    Pages.Add(id, new DNNavigationPage(new LoginPage(id)));
                    break;
                case MenuType.Item3:
                    Pages.Add(id, new DNNavigationPage(new LoginPage(id)));
                    break;
                //...
            }
        }
        newPage = Pages[id];
        if (newPage == null)
            return;
        //...
        Detail = newPage;
        //...
    }
    

    This allows me to specify colors of this NavigationPage:

    public class DNNavigationPage : NavigationPage
    {
        public DNNavigationPage(Page root) : base(root)
        {
            Init();
        }
        public DNNavigationPage()
        {
            Init();
        }
        void Init()
        {
            BarBackgroundColor = Color.FromHex("#2D3540");
            BarTextColor = Color.White;
        }
    }
    

    The other part of the sample is dedicated to apply the MaterialDesign theme to the Android project, by:

    • defining the style's properties of the theme in 3 files: "Resources/values/colors.xml", "Resources/values/style.xml" and "Resources/values-v21/style.xml"
    • apply the theme in the AndroidManifest file by adding: android:theme=”@style/MyTheme”
    • customizing the Toolbar and Tabs by adding 2 axml files: "Resources/layout/tabs.axml" and "Resources/layout/toolbar.axml"
    • replacing the "FormsApplicationActivity" by "FormsAppCompatActivity" in the "MainActivity.cs"

    So if I simply define my RootPage as the Mainpage in App.cs, there is no problem:
    MainPage = new RootPage();

    But I try to use MVVMLight, with Navegar that I've already used in another project. Navegar is a package that allows to manage easily a ViewModel first navigation:
    https://nuget.org/packages/Navegar/

    In this case, I must specify the start page of the app as the first NavigationPage: so the page is registered in the navigation stack as the entry of this app:
    MainPage = (NavigationPage)ServiceLocator.Current.GetInstance<INavigation>().InitializeRootFrame<RootPageViewModel, RootPage>();
    With this, I meet several cases:

    • if I don't apply the MaterialDesign theme, and that I use "FormsApplicationActivity", I get an exception:

    • if I apply the MaterialDesign theme, and that I use "FormsAppCompatActivity", there is no exception, but there is a little "gap" between the Detail page and the toolbar:

      This gap only occurs if the OS version of Android is >= 21...

    => Do you have an explanation about these 2 problems?

    @AdamP
    I also donwloaded your sample:
    https://github.com/adamped/xarch-starter
    But I don't understand how you manage the relation between the items of the MenuPage and the displayed Detail page...
    In addition, I can't build the Android project, whereas there is no problem in iOS or WP:

    => Could you help me?

  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭
    edited April 2016

    @Pierre-ChristopheDus

    1 - With your navigation setup, if you have a master detail page that you assign to the MainPage. The detail page will have 1 NavigationPage and you will call that to navigate between pages. Hence your menu items in the master page will be called in the 1 NavigationPage to move to a different page.

    2 - FormsAppplicationActivity doesn't support nested NavigationPages, however I didn't realize that FormsAppCompatActivity allowed it.

    Normally if you have such a situation I recommend trying to simplify it so that you only have 1 NavigationPage visible on the screen at a time.

    3 - In the properties of the Android project, in Android Options do you have Enable developer instrumentation under Debugging options ticked?

    And just to confirm you are trying to debug on an emulator not a real device? if its a real device an you let me know the model number?

  • Pierre-ChristopheDusPierre-ChristopheDus ✭✭✭ FRUniversity ✭✭✭

    @AdamP
    Thanks for your return.

    1 - I know that my implementation of the navigation is not very clear:

    • but I would like the MasterDetailPage be registered as the first page of the stack
    • I also would like to use the MaterialDesign for Android
      That's why I tried to combine the implementation of the sample Hanselman.Forms and the use of Navegar

    2 - In this case, what is the best approcach to adopt for doing this?

    • if I want to keep the implementation of the details pages from the Hanselman.Forms project, how do I handle navigation between a detail page, and ths "childs" pages in the application?
    • is it possible to return back to the MasterDetailPage, even if it is not included in the navigation stack?

    3 - I finally managed to build your project both on a simulator and my device, without changing anything...

    • but how edit the rendering of the MasteDetailPage?

      => I would like that the Master pane takes all thet height, and that the Navigation bar doesn't show the app icon, like in the other project:

    • could you explain me how you build the menu items of the Master page, and how to bind these items to the associated Detail page?

  • BrenoSantos.9720BrenoSantos.9720 BRMember
    edited June 2016

    Guys, take a look at this:
    https://github.com/NaylahProject/Naylah

    Resolves X,N,Z problems... N approaches too, splash, login, app phases, "hamburguer menu", etc...

    Just run sample app... I'm writing the docs.

  • PubuduGayanPubuduGayan ✭✭✭ NZMember ✭✭✭

    @AdamP ok this might not relevant to this thread but I choose this because its related to navigation. Adam do you thing is it a best practice to use partial view in xamarin form. if its good how you navigate back to partial view using navigation? because you can't do it in normal way sin;t it, if you push to the navigation stack it throw error parent missing which understandable.

  • AdamPAdamP ✭✭✭✭✭ AUUniversity ✭✭✭✭✭

    Hi @PubuduGayan - Its best if you ask a New Question on the forum. You can then also tag me in @AdamP if you want me to have a look at it.

  • N.Y.GudlanurN.Y.Gudlanur USMember

    Hi,
    Below code is working fine for Navigation in MVVM model concept.
    Xamarin.Forms.Application.Current.MainPage.Navigation;

  • TaofeekLamidiTaofeekLamidi USMember

    using this in loginviewmodel to move to another page not working.
    NavigationPage _navPage = new NavigationPage(new LoginPage());
    // Assign your NavigationPage to your MasterDetail
    App.NavPage = _navPage;

            // When you want to move
            await _navPage.PushAsync(new RegisterPage());
    
  • DeveshMishraDeveshMishra ✭✭ USMember ✭✭

    @AdamP said:
    @FranciscoGG - it all depends on what framework you are using. Things such as MVVMLight has a navigation service you can use but if we remove all frameworks for the time being here is how I would do it.

    In your App.cs you would have something similar to this code (apologies if it doesn't compile, I am coding this off the top of my head).

    public static NavigationPage NavPage = null;
    
    public App()
    {
        var _navPage = new NavigationPage(new PageOne());
    
        var _masterPage = new MasterDetailpage();
        _masterPage.Detail = NavPage;
        _masterPage.Master = (your master / menu page);
    
        MainPage = _masterPage;
    
    }
    

    Then in your code you can reference

    await App.NavPage.PushAsync(new PageTwo());

    That gets you functional. However you don't want to access the NavigationPage this way. You can use DependencyInjection to create a NavigationService.

    Something like

    public class NavigationService: INavigationService
    {
    
          public NavigationService(NavigationPage navPage)
          {
                // Keep local copy of navPage
          }
    
          public async Task NavigateTo(Page page)
          {
    
                await _navPage.PushAsync(page);
    
          }
    
          public async Task GoBack()
          {
         
                await _navPage.PopAsync();
     
          }
    
    }
    

    Then you can use the ServiceLocator pattern or constructor injection to get the instance of the navigation service.

    Now you should probably even go one step further and register your PageTypes against string key's so that you can just pass a string through and have it construct a Page and move to it. However there are many frameworks that do that stuff for you. MVVMLight, MVVMCross, FreshMVVM, etc. It would be wise to start up with one of them.

    Hi Adam,

    I found your solution very helpful, specially it helps me to manage pages using a single instance of Navigation Page. But there is one issues, when i select any item in Master Page and navigate to any other page, i can't see MasterDetail icon (i.e. three vertical lines). Instead of that, i can see there is one back button. I tried to hide it, but i can't see that icon over there. But my Master page is there, if i swipe from left to right then it appears. Is there any solution for that ?

  • DamienDoumerDamienDoumer USMember
    edited July 2017

    Hi Adam, I think this is a solution to your problem,
    with this piece of code, you will be able to navigate in a MasterDetails page, without leaving the page its self, no Back arrow nothing.
    var page = Application.Current.MainPage as MasterDetailPage;
    page.Detail = new NavigationPage(new HomePage());

    You can put it in aNavigation service, or what ever and Obey the MVVM pattern... I hope it helps.

  • ChristopheGIGAXChristopheGIGAX FRMember
    edited November 2017

    Thanks it helped me !

  • josemariajosemaria USMember

    Hello:
    if i use:

    var page = Application.Current.MainPage as MasterDetailPage;
    page.Detail = new NavigationPage(new HomePage());

    what can i do if i want to respect mvvm pattern?.

    I need to load homeviewmodel insted of homepage without navigate to anotherpage (if i use pushasync i am loosing hamburguer menu buf if use detail=NavigationPage(new HomePage() HomeViewModel is not loading.).

Sign In or Register to comment.