I have a Xamarin.Forms application with MyWebViewPage : ContentPage, which set in the App.xaml.cs as MainPage. Every time when I use await Navigation.PushAsync(new MyWebViewPage()) for transition on the MainPage from other pages in the application, a new instance of type MyWebViewPage has created in memory. Can I realize transition on the existing instance of page MyWebViewPage without creating new instance of this type? I use media player on this page which linked with SignalR and this player plays sounds as many times as instances of this type was created. So I need to have only one instance of this type. Thank you for any help.
Have you tried saving the instance and navigating to the same instance? So instead of doing:
await Navigation.PushAsync(new MyWebViewPage());
You do:
await Navigation.PushAsync(_webViewPage ??= new MyWebViewPage());
Or if you don't like the ternary:
if (_webViewPage == null)
_webViewPage = new MyWebViewPage();
await Navigation.PushAsync(_webViewPage);
Related
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.
I am writing a Xamarin app. In the App.Xaml.cs I expose some events.
I set up a Main Page like this:
var nav = new NavigationPage(new MainPage());
MainPage = nav;
Then from the Main Page I push another page:
void DevicePictureBox_Click(object sender, EventArgs args)
{
Image imse = (Image)sender;
String NameofDevice =
devicesHandlesDictionary_byDevicepictureID[imse.Id].Name;
var p = new DevicesPage(NameofDevice);
Navigation.PushAsync(p);
The DevicesPage subscribes to an event (exposed from App).
Setting up some Debug.WriteLine along the code I discovered that DevicesPage is still active when navigate back to MainPage (pressing the back arrow or with Navigation.PopAsync()).
Also when I navigate to DevicesPage again the code is executed twice (Degug.WriteLine string in the Output window appear twice) and 3,4,5 etc. times if I keep going back and forward. It seems like a new page gets created every time and the previous still run in background.
I understand that events should be unsubscribed (in that case where should be done). Also it baffles me that the code still seems to be running in the page navigated back. How to remove the page?
DevicesPage is still active when navigate back to MainPage
Just because you navigate away from a page doesn't mean you're done with it. You may want to re-use it later. You can explicitly clean up it's resources if you need to (for instance, unsubscribe your event handlers in OnDisappearing) but this doesn't happen automatically
It seems like a new page gets created every time
it does, because you are explicitly creating a new instance of DevicesPage each time
var p = new DevicesPage(NameofDevice);
Navigation.PushAsync(p);
there is nothing preventing you from keeping a reference to a DevicesPage instance and re-using it instead of always creating a new one
Is there any reason or problem I cant call InitializeComponent method in OnAppearing function of a page in xamarin forms project?
I understand that I must call InitializeComponent only once to create the actual page. But what if I check that Content is already created and do it as below. Is it a bad implementation or practice? because it is said that no xaml based application does it and always call it in a constructor of the page.
reason I want to do it as below because xamarin.forms start up time is slow running on Android and if you use Masterdetail page(I think same for tabbed page), you must initialize it at the start up, it causes every navigation page defined in masterdetail page to be initialized and it costs you 2-3 secs depending on your UI could be even higher cost. any thoughts or experiences on this?
protected override void OnAppearing()
{
if (Content == null)
{
InitializeComponent();
}
}
I do not recommend this approach. From the xamarin docs.
The constructor of that class calls InitializeComponent, which then calls the LoadFromXaml method that extracts the XAML file (or its compiled binary) from the PCL. LoadFromXaml initializes all the objects defined in the XAML file, connects them all together in parent-child relationships, attaches event handlers defined in code to events set in the XAML file, and sets the resultant tree of objects as the content of the page.
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/
If your forms are too slow on Android I would enable Fast Renders instead
In the OnCreate of your main activity add this line of code just before Xamarin.Forms.Init
Forms.SetFlags("FastRenderers_Experimental");
https://xamarinhelp.com/xamarin-forms-fastrenderers-android/
It would not hurt to try to use compiled Xaml also
using Xamarin.Forms.Xaml;
...
[XamlCompilation (XamlCompilationOptions.Compile)]
public class HomePage : ContentPage
{
https://developer.xamarin.com/guides/xamarin-forms/xaml/xamlc/
I want to confirm if I am using the prism navigation service with xamarin forms correctly. I have a master detail page, a styled navigation page and a bunch of content pages.
right now I am using the service in the following way:
var prj = await dataService.GetLwdProject(appState.SelectedProjectId);
var nparam = new NavigationParameters();
nparam.Add("Project", prj);
await NavigateTo("RootPage/StyledNavigationPage/SessionsListPage", nparam);
Where the Master detail page is the RootPage object. So expecting that when a user selects an item from this list page the correct way to the service should be:
var nparma = new NavigationParameters();
nparma.Add("Session", option);
await App.NavigateTo("RootPage/StyledNavigationPage/SessionsListPage?ProjectId=" + option.ProjectId + "/LocationListPage", nparma);
What I expect that just a LocationListPage would be added to the navigation stack, but when I use the the hardware back button on android it looks like that not only the last page was added but the whole path (all pages). So is this the correct way auto construct the desired path?
No. Navigation is always relative to where you are calling it. What you have now will navigate to the entire deep link you have created every time. Just navigate to your target NavigationPage/SessionListPage and pass your parameter. Though, you won't get a new page every time in this case, since you are navigating to the same view, but just passing different state.
We have a couple of Fragments that we use as common controls:
MyCommonHeaderA
MyCommonHeaderB
In our common View class we call base.OnCreate(bundle) and once that has returned we fish out the fragment instances and set their ViewModels
var commonHeaderAFragment = (MyCommonHeaderA)this.SupportFragmentManager.FindFragmentById(Resource.Id.header1banner);
if (commonHeaderAFragment != null)
{
commonHeaderAFragment.ViewModel = this.ViewModel;
}
var commonHeaderBFragment = (MyCommonHeaderB)this.SupportFragmentManager.FindFragmentById(Resource.Id.header2banner);
if (commonHeaderBFragment != null)
{
commonHeaderBFragment.ViewModel = this.ViewModel;
}
Until recently this has been working with no problem. Recently we have upgraded Xamarin and MVVMCross.
Now whenever we rotate the device OnCreate is called and the execution path ends up in MvxFragmentExtensions.OnCreate where it tries to lookup a type for the Fragment using FindAssociatedViewModelTypeOrNull. There is no associated ViewModel type for the Fragment. We never needed to, should we have associated a type?
I did try MvxViewForAttribute and concrete typed ViewModel property but neither of those worked as they wanted to create new VM instances.
I have a solution which is that in the base OnCreate, if we have a bundle try and find the Fragments and set their ViewModel property before base.onCreate and when there is no bundle we set the ViewModel property after OnCreate. It is clunky but works. I just wanted to check if we should have been setting up our Fragments differently so that we would not have hit this issue
There is an example available that uses the MvxCachingFragmentActivity: https://github.com/MvvmCross/MvvmCross-AndroidSupport/tree/master/Samples
In there you don't need to worry about those kind of problems anymore.