FreshMVVM Switching Navigation Stacks Between ContentPage and TabbedPage

543 views Asked by At

I'm converting an app to use FreshMVVM from a non-MVVM format. When the app launches, there is a login page, then after login, a tabbed page with modal pages called from the buttons on each tabbed page.

Given the process I'm following, I figured that this would be a perfect option to use Michael Ridland's process to switch out NavigationStacks (https://github.com/rid00z/FreshMvvm#switching-out-navigationstacks-on-the-xamarinforms-mainpage), but the steps appear to be missing quite a few important instructions, and these steps are not in the sample app on the GitHub.

In my App.xaml.xs file, I have the following code:

public App()
        {
            InitializeComponent();
            var loginPage = FreshMvvm.FreshPageModelResolver.ResolvePageModel<LoginPageModel>();
            var loginContainer = new STNavigationContainer(loginPage, NavigationContainerNames.AuthenticationContainer);
            var homePageViewContainer = new FreshTabbedNavigationContainer(NavigationContainerNames.MainContainer);

            MainPage = loginContainer;
        }

        public FreshTabbedNavigationContainer(string navigationServiceName)
        {
            NavigationServiceName = navigationServiceName;
            RegisterNavigation();
        }

        protected void RegisterNavigation()
        {
            FreshIOC.Container.Register<IFreshNavigationService>(this, NavigationServiceName)
        }

        public void SwitchOutRootNavigation(string navigationServiceName)
        {
            IFreshNavigationService rootNavigation =
                FreshIOC.Container.Resolve<IFreshNavigationService>(navigationServiceName);
        }
        public void LoadTabbedNav()
        {
            var tabbedNavigation = new FreshTabbedNavigationContainer();
            tabbedNavigation.AddTab<CharactersPageModel>("Characters", "characters.png");
            tabbedNavigation.AddTab<AdventuresPageModel>("Adventures", "adventures.png");
            tabbedNavigation.AddTab<AccountPageModel>("Account", "account.png");
            MainPage = tabbedNavigation;
        }
 public class NavigationContainerNames
    {
        public const string AuthenticationContainer = "AuthenticationContainer";
        public const string MainContainer = "MainContainer";
    }

This seems to match with the steps provided in the ReadMe.md, but the calls to NavigationServiceName return an error, since there's no instruction on creating such a class or what it should contain, nor am I clear on where the FreshTabbedNavigationContainer or SwitchOutRootNavigation would be called.

Has anyone been able to get this to work? What steps am I missing on this?

EDIT: I forgot that I have extended the FreshNavigationContainter class and the FreshNavigationPage class. Here are my extended classes:

STNavigationContainer:

public class STNavigationContainer : FreshNavigationContainer
    {
        public STNavigationContainer(Page page) : base(page)
        {
        }

        public STNavigationContainer(Page page, string navigationPageName) : base(page, navigationPageName)
        {
        }

        protected override Page CreateContainerPage(Page page)
        {
            if (page is NavigationPage || page is MasterDetailPage || page is TabbedPage)
                return page;

            return new STNavigationPage(page);
        }
    }

STNavigationPage:

public class STNavigationPage : NavigationPage
    {
        public STNavigationPage()
        {
                
        }

        public STNavigationPage(Page page) : base(page)
        {
            BarBackgroundColor = Color.FromHex("#2DAFEB");
            BarTextColor = Color.FromHex("#C9371D");
        }
    }

Edit 2: Re-reading https://michaelridland.com/xamarin/implementing-freshmvvm-mvvm-xamarin-forms/ and the Github post, I was able to figure out that I needed to do this in a Custom Navigation Service, so here is my updated code.

App.xaml.cs:

    public partial class App : Application
    {
        public static Account account = new Account();

        public App()
        {
            InitializeComponent();
            FreshIOC.Container.Register<IDatabaseService, DatabaseService>();

            if (account.Equals(null))
            {
                LoadSingleNav();
            }
            else
            {
                LoadTabbedNav();
            }

            var navPage = new NavigationPage(new LoginPage())
            {
                BarBackgroundColor = Color.FromHex("#2DAFEB"),
                BarTextColor = Color.FromHex("#C9371D")
            };
            NavigationPage.SetHasNavigationBar(navPage.CurrentPage, false);
            MainPage = navPage;

            MainPage = loginContainer;
        }

        public FreshTabbedNavigationContainer(string navigationServiceName)
        {
            NavigationContainerNames = navigationServiceName;
            RegisterNavigation();
        }

        protected override void OnStart()
        {
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }

CustomNavService.cs:

    public class CustomNavService : NavigationPage, IFreshNavigationService
    {
        FreshTabbedNavigationContainer _tabbedNavigationPage;
        Page _charactersPage, _adventuresPage, _accountPage;
        public CustomNavService(Page page) : base (page)
        {
            NavigationServiceName = "CustomNavService";
            LoadTabbedNav();
            CreateLoginPage();
            RegisterNavigation();
        }

        public string NavigationServiceName { get; private set; }

        public void NotifyChildrenPageWasPopped()
        {
            throw new NotImplementedException();
        }

        public async Task PopPage(bool modal = false, bool animate = true)
        {
            if (modal)
                await Navigation.PopModalAsync (animate);
            else
                await Navigation.PopAsync (animate);
        }

        public async Task PopToRoot(bool animate = true)
        {
            await Navigation.PopToRootAsync(animate);
        }

        public async Task PushPage(Page page, FreshBasePageModel model, bool modal = false, bool animate = true)
        {
            if (modal)
                await Navigation.PushModalAsync(page, animate);
            else
                await Navigation.PushAsync(page, animate);
        }

        public Task<FreshBasePageModel> SwitchSelectedRootPageModel<T>() where T : FreshBasePageModel
        {
            IFreshNavigationService rootNavigation =
                FreshIOC.Container.Resolve<IFreshNavigationService>(NavigationServiceName);
        }

        public void LoadTabbedNav()
        {
            _tabbedNavigationPage = new FreshTabbedNavigationContainer();
            _charactersPage = _tabbedNavigationPage.AddTab<CharactersPageModel>("Characters", "characters.png");
            _adventuresPage = _tabbedNavigationPage.AddTab<AdventuresPageModel>("Adventures", "adventures.png");
            _accountPage = _tabbedNavigationPage.AddTab<AccountPageModel>("Account", "account.png");
            this = _tabbedNavigationPage;
        }
        private void CreateLoginPage()
        {
            var loginPage = FreshPageModelResolver.ResolvePageModel<LoginPageModel>(null);
            var loginContainer = new STNavigationContainer(loginPage, CustomNavService.NavigationContainerNames.AuthenticationContainer);
            var homePageViewContainer = new FreshTabbedNavigationContainer(CustomNavService.NavigationContainerNames.MainContainer);

        }

        protected void RegisterNavigation()
        {
            FreshIOC.Container.Register<IFreshNavigationService>(this, NavigationServiceName);
        }

        public class NavigationContainerNames
        {
            public const string AuthenticationContainer = "AuthenticationContainer";
            public const string MainContainer = "MainContainer";
        }

    }

The SwitchSelectedRootPageModel<T> task in my Custom Navigation Service is showing that a return is needed, and I'm not quite clear on what the LoadTabbedNav is supposed to include with the this; the example shows this.Detail, but that's showing as an invalid reference.

1

There are 1 answers

0
Gaz Winter On

Looking through our FreshMvvm implementation, our SwitchSelectedRootPageModel looks like this:

public Task<FreshBasePageModel> SwitchSelectedRootPageModel<T>() where T : FreshBasePageModel
{
    return Task.FromResult<FreshBasePageModel>(null);
}

Our code correctly uses

this.Detail = _tabbedNavigationPage;

So if you are getting an invalid reference, there must be something else missing.