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.
Looking through our FreshMvvm implementation, our SwitchSelectedRootPageModel looks like this:
Our code correctly uses
So if you are getting an invalid reference, there must be something else missing.