Xamarin Forms Navigation.PushAsync works only once - xamarin

In our Xamarin Forms app we have multiple pages. When we navigate to an other page for the first time from the main navigation page it works as expected. When we then use the back button (in the app or the Android system button) and navigate again to the next page then it doesn't load. It changes the title but it shows an empty page.
It does not matter what page you use as first page to go to and what page is the second page to go to. It also doesn't work if the first and second page is the same page.
In the app we initialize the main page as followed:
MainPage = new NavigationPage(new MainPage());
The MainPage is an TabbedPage.
We just navigate like this:
if (Application.Current.MainPage is NavigationPage mainPage)
{
await mainPage.Navigation.PushAsync(new DetailPage());
}
This worked before, but I think that during the update of Xamarin.Forms from 4.2.0.848062 to 4.3.0.947036 it broke. But I cannot find anything that could brake this is release notes.
== Edit ==
We saw some inconsistency in all the calls to navigate. So we created an helper class to do all the navigation. This now looks like this:
public static class NavigationHelper
{
public static void NavigateTo(Page page)
{
if (Application.Current.MainPage is NavigationPage mainPage)
{
Device.BeginInvokeOnMainThread(async () =>
{
await mainPage.Navigation.PushAsync(page);
});
}
}
}
So we now only navigate like this:
NavigationHelper.NavigateTo(new DetailPage());
But this still doesn't fix the issue. The first time we get the correct page, the second navigation we get an empty screen. The title in the navigation bar is changing to the page title where we navigate to. But the rest keeps empty.

Are you remembering to use the "Navigation" of the (NavigationPage)Application.Current.MainPage; - and not the current pages Navigation?

I've found it with some help. In the Output Window we found the message
System.ObjectDisposedException: Cannot access a disposed object.
This happened when we went back to the previous page and made it impossible to go to the next page. Turned out to be an bug in the view model. We where trying to add items to an observable list that was already disposed. Fixing this fixed the navigation issue.

Related

Xamarin Shell: Redirect to different page and remove current page from nav stack

I imagine this is pretty straightforward, but I'm struggling to make sense of how to do it via the documentation...
I've got a tab page, that is opened from a flyout item.
The tabs for this page are dynamically loaded from saved data in the code behind, and all works exactly as expected.
However, in the scenario when there is no saved data (and thus, no tabs) I want to redirect the user to a different page, and crucially, not be able to navigate back to this tab page (but allow going back to the page they were initially on BEFORE they navigated to the tabbed page)
I've got a method in initalize for the tabbed page that, if it doesn't have any saved data, attempts to do this. I started with:
Shell.Current.Navigation.PopAsync();
Shell.Current.GoToAsync(nameof(AddNewDataPage));
which navigated to the add page, but pressing the back button just resulted in the add page being shown again, over and over.
I then tried:
Shell.Current.Navigation.PopToRootAsync();
Shell.Current.GoToAsync(nameof(AddNewDataPage));
which did the same.
So next I went with trying to navigate backwards:
Shell.Current.GoToAsync("../" + nameof(AddNewDataPage));
which showed the right page again, but now the back button doesn't work
Next, I went with trying an absolute route:
Shell.Current.GoToAsync("//HomePage/" + nameof(AddNewDataPage));
which worked... sort of.
The first time the user clicks the flyout for the tabbed page, it all works great. back button takes you to the home page etc... but the second and subsequent times the user clicked the flyout, they navigate to the tabbed page and my LoadData method isn't called.
I assumed this is because the tabbed page is still loaded, so I added:
protected override void OnAppearing()
{
LoadData();
}
Now, when the user clicks the flyout for the second and subsequent times, they navigate to the HomePage page instead of the AddNewDataPage page (an improvement, I guess?)
So, now I'm at a loss.. it seems like this should be really simple, but I can't figure it out.
You can try this.
//route The route hierarchy will be searched for the specified route, upwards from the current position. The matching page will replace the navigation stack.
https://learn.microsoft.com/nl-nl/xamarin/xamarin-forms/app-fundamentals/shell/navigation?WT.mc_id=friends-0000-jamont#relative-routes
await Shell.Current.GoToAsync($"//{nameof(AddNewDataPage)}");
https://www.youtube.com/watch?v=ylbgWHB_gMI&t=703s&ab_channel=JamesMontemagno
An explanation about relative routes.
May I don't understand your problem clearly. But I created a simple with the flyout shell app.
At first, it seems that the construction method of the content page will jut call once which the page shows first time. So I put the following code into it. Such as:
public ItemsPage()
{
InitializeComponent();
BindingContext = _viewModel = new ItemsViewModel();
Shell.Current.Navigation.PopAsync();
Shell.Current.GoToAsync(nameof(NewItemPage));
}
And then when the app got into the ItemsPage first time, it will get into the NewItemPage. User can add new item here. After that, when I clicked the back button, I will get into the ItemsPage back without it's construction method called. So you can't add the LoadData(); in it.
You can put it into the OnAppearing method. Such as:
protected override void OnAppearing()
{
//LoadData();
if (_viewModel.Items.Count < 8)
{
Shell.Current.Navigation.PopAsync();
Shell.Current.GoToAsync(nameof(NewItemPage));
// then add two item in the NewItemPage
}
else {
base.OnAppearing();
_viewModel.OnAppearing();
}
}
This will get the same effect as above. I also try to move the Shell.Current.Navigation.PopAsync();. The result is same. It will get back to the ItemsPage when you clicked the back button in the NewItemPage after you add two item.
which navigated to the add page, but pressing the back button just resulted in the add page being shown again, over and over.
You may just need a if() to judge the condition the app need to go to the AddNewDataPage with skipping the tab page.

Xamarin Forms MasterDetailPage - Secondary menu drawn twice on iOS

My app uses Master-Detail navigation.
I push pages on the navigation stack to have back button functionality.
When pushing a page with ToolbarItems (secondary menu), that's fine on Android, but it doubles the menu items on iOS.
Switching to the same page (not pushing), there is no problem on iOS (secondary menu shown once, as expected).
I adopted the official MasterDetail sample (https://github.com/xamarin/xamarin-forms-samples/tree/master/Navigation/MasterDetailPage) to show what I mean, see solution zip file.
Compile and start it on iOS device, press the button on the first page to open a sub page, and you should see doubled menu items (actually one which is drawn twice), so there are two rows of secondary menu items visible instead of just one.
The two rows are identical, both rows (and their menu items) react when tapping.
If you select the Reminder page through the Hamburger menu, the secondary menu is fine (even on iOS), as the page is not pushed on the navigation stack but switched.
Some interesting parts:
ContactsPage.xaml.cs:
SwitchToDetailPageAndPushOnNavStack()
ReminderPage.xaml:
<ContentPage.ToolbarItems>
<ToolbarItem Text="Do something" Clicked="OnDoSomethingClicked" Order="Secondary" />
</ContentPage.ToolbarItems>
What can I do to get rid of this weird effect on iOS?
It looks like you are adding a new NavigationPage which seems to be causing the issue. You block the extra nav bar with:
NavigationPage.SetHasNavigationBar(newPage, false);
but still the added toolbar item is getting added twice.
If instead you do the usual way of pushing a page, then this issue disappears. E.g. in ContactsPage.xaml.cs file, change:
private void OnOpenSubPageClicked(object sender, EventArgs args)
{
Device.BeginInvokeOnMainThread(() =>
{
MainPage mainPage = App.Instance.MainPage as MainPage;
if (null != mainPage)
mainPage.SwitchToDetailPageAndPushOnNavStack(mainPage.MasterPage.RemindersPageItem);
})
}
to:
private async void OnOpenSubPageClicked(object sender, EventArgs args)
{
await Navigation.PushAsync(new ReminderPage());
}
That seems to work as expected.
I had a similar issue. Posting here in case anyone else finds themselves in this bind.
I was getting the doubled toolbar when I had this code in App.cs...
MainPage = new NavigationPage(new MyApp.Home());
But changing it to
MainPage = new MyApp.Home();
solved the issue for me.

When using navigationpage's InsertPageBefore method toolbar items disappear on iOS

When I navigate to a page that uses a flow of NavigationPage.Navigation.InsertPageBefore(ContentPage) then NavigationPage.PopAsync().ConfigureAwait(false) the toolbar item on the global navigation page disappears but when using push/pop the tool bar always remains. This only happens on iOS.
The navigation page is a global scoped to the app level along with a master detail page in which the navigation page serves as the detail portion.
Add ToolbarItem
var toobaritemMenu = new ToolbarItem
{
Icon = "Screenshot",
Command = new Command(() => Device.BeginInvokeOnMainThread(() => App.MasterNavigation.PushAsync(new ContentPage(), false)))
};
App.MasterNavigation.ToolbarItems.Add(toobaritemMenu);
Moving to a Page Like This Causes Toolbar Item to Disappear on iOS
Device.BeginInvokeOnMainThread(() => App.MasterNavigation.Navigation.InsertPageBefore(new ContentPage(), App.MasterNavigation.CurrentPage));
Device.BeginInvokeOnMainThread(() => App.MasterNavigation.PopAsync().ConfigureAwait(false));
Is there a reason for this as it seems inconsistent since it works on Android.
I ran into the same issue. My workaround was to change how I was manipulating the navigation stack: instead of inserting the new page and popping off the current page, push the new page onto the stack and then remove the current one:
await currentPage.Navigation.PushAsync(nextPage, animated: true);
currentPage.Navigation.RemovePage(currentPage);
You end up with the same final navigation stack, but it avoids whatever issue is causing the problem with the secondary toolbar items in iOS.

Navigating back and forth not working

hi i created an app which contains three pages,
if go from MainPage to Page1 and reversely 3 to 4 times it ts working fine, but when i Navigate from Mainpage to Page 2 it is working but when i navigate back from Page 2 to Main Page it is opening MainPage after 1 second it is going back to Page2
can you please tell me the solution for this
Okay i am going to give this a wild guess seeing that you have not given code ,
on each page there is a override method you can use, onNavigatedTo.
what i will do is put an OnNavigatedTo event on each page and put a break point in that event and step to see what each individual page does, here is a quick example where i use onnavigated to test if the navigation to this page was a 'back' navigation!
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
//Check if navigation was back!
// you can add the breakpoint here if you want to.
if (e.NavigationMode == NavigationMode.Back)
{
messagebox.show("Nav is back!");
}
now you can easily see what each individual page does when navigated to :) hope this helps a bit.

Remove a page from Navigation Stack

I have this application schema :
[List Page] -> [Detail Page] -> [ShareOnFacebook Page]
^__________________|
The user select an Item in the [List Page]
The user may or may not click a Share button if he or she does, the application navigates to a [ShareOneFacebook page] which displays a Facebook Login Form, posts a message and navigates back
The user navigates back.
If the user shared on facebook, it will "repost" the message, because the application store the session so it will navigate back to ShareOnFacebook, and then back to my Detail page.
If the user didn't share, he goes back to the List Page.
How can I "ommit" the ShareOnFacebook page in my navigation stack ?
Try this: Call NavigationService.RemoveBackEntry(); in the OnNavigatedTo method. This will remove the previous page from the stack. In my opinion the trick with Navigation.GoBack(); is not satisfying because it shows the page to remove for a short time.
Note: Works only with Windows Phone OS 7.1 (Mango) SDK
I have a similar situation in my app, i solve it with a very simple solution.
If you want to "skip" a page in your backstack, place some logic in the NavigatedTo() function of that page.
For example: you can have a bool that you set to true when you post to facebook, and then place the following code in the NavigatedTo() function of the ShareOnFacebook page.
Here is pseudo code:
if (alreadyPosted)
Navigation.GoBack();
The GoBack() function will skip this page and return to the previous one, the user will never see the page.
Have a look at simple library i wrote for such purposes:
http://navcoerce.codeplex.com/
var fluent = new FluentNavigation(RootFrame);
fluent.WhenNavigatedTo<MainPage>()
.ThenTo<LoginPage>()
.ThenToAnyPage()
.RemoveEntriesFromBackStack(1);
fluent.WhenNavigatedTo<MainPage>()
.ThenTo<LoginPage>()
.ThenTo<RegisterPage>()
.ThenTo<PaymentPage>()
.RemoveEntriesFromBackStackTill<MainPage>();
fluent.WhenNavigatedTo<MainPage>()
.ThenTo<SecondPage>()
.ThenTo<RegisterPage>()
.ThenOptionallyTo<ForgotPasswordPage>()
.ThenToAnyPage()
.RemoveEntriesFromBackStackTill<MainPage>();
fluent.WhenNavigatingTo<PaymentPage>()
.RedirectTo<LoginPage>();
fluent.WhenNavigatingTo<PaymentPage>()
.If(() => false)
.RedirectWithReturnUri<LoginPage>("ReturnUri");
Have a look at the new Nonlinear Navigation Service recipe
Download it from http://create.msdn.com/en-us/education/catalog/article/nln-serv-wp7
I use the removeBackEntry method on the NavigationService Class. I also use this as a way to setup my own splash screens
private void BWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// switch screen
NavigationService.Navigated += new NavigatedEventHandler(NavigationServiceNavigated);
NavigationService.Navigate(new Uri("/Pages/main.xaml", UriKind.Relative));
}
void NavigationServiceNavigated(object sender, NavigationEventArgs e)
{
NavigationService.RemoveBackEntry();
}
I posted an example for the same issue here.
The Nonlinear Navigation Service Matt linked to essentially does the same thing but would probably be better than my simple example.

Resources