I have a TabbedPage in Xamarin.Forms:
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var playPage = new NavigationPage(new PlayPage())
{
Title = "Play",
Icon = "play1.png"
};
var settingsPage = new NavigationPage(new SettingsPage())
{
Title = "Settings",
Icon = "settings.png"
};
var aboutPage = new NavigationPage(new AboutPage())
{
Title = "About",
Icon = "about.png"
};
Children.Add(playPage);
Children.Add(settingsPage);
Children.Add(aboutPage);
}
Each of the child pages are a ContentPage that overrides the OnAppearing method. The content of my child pages are not updating properly when I navigate between the tabs and further debugging tells me that the OnAppearing method for child pages are only called once (when the MainPage is first loaded).
Anyone have any idea why the OnAppearing method is only called once? And how can I solve this issue?
More Info
My child pages such SettingsPage contains events that when fired open another ContentPage using Navigation.PushAsync method. I expected the OnAppearing method to also get called when switching from tab pages and the navigation pages within those tab pages. (Hope this makes sense)
It only triggers once since the View is not destroyed when you navigate between tabs.
You can use the CurrentPageChanged event to figure our that a page has changed and signal the view it changes to and do whatever update of the view you need.
this.CurrentPageChanged += PageChanged;
void PageChanged(object sender, EventArgs args)
{
var currentPage = CurrentPage as MyTabPage;
currentPage?.UpdateView();
}
Then in your page:
public void UpdateView()
{
// do whatever you need here to update the page
}
Do not embedded your ContentPages within a "single" level NavigationPage.
The following works fine in terms of the OnAppearing event firing when switching between tabs:
public class PlayPage : ContentPage
{
protected override void OnAppearing()
{
System.Diagnostics.Debug.WriteLine("PlayPage");
base.OnAppearing();
}
}
public class AboutPage : ContentPage
{
protected override void OnAppearing()
{
System.Diagnostics.Debug.WriteLine("AboutPage");
base.OnAppearing();
}
}
public class SettingsPage : ContentPage
{
protected override void OnAppearing()
{
System.Diagnostics.Debug.WriteLine("SettingPage");
base.OnAppearing();
}
}
public partial class MainPage : TabbedPage
{
public MainPage()
{
var playPage = new PlayPage() { Title = "Play" };
var settingsPage = new SettingsPage() { Title = "Settings" };
var aboutPage = new AboutPage() { Title = "About" };
Children.Add(playPage);
Children.Add(settingsPage);
Children.Add(aboutPage);
}
}
public class App : Application
{
public App()
{
this.MainPage = new MainPage();
}
}
Related
I have a search page that have a Button to navigate to another page . When I click on that Button it takes about 4 seconds to navigate .
SearchPage Command :
Device.BeginInvokeOnMainThread(async () =>
{
await (Application.Current.MainPage as NavigationPage).PushAsync(new OtherPage());
});
OtherPage Code Behind :
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class OtherPage : ContentPage
{
SearchStateViewModel Model;
public SearchEstatePage()
{
InitializeComponent();
Model = new SearchStateViewModel();
Layout.BindingContext = Model;
}
protected override void OnAppearing()
{
base.OnAppearing();
Model.GetCities();
}
}
What is the problem ?
As #MichaelRandall advice I have checked OtherPage's XAML and finde that the Syncfusion's components in XAML slows down the navigation .
But what what is the replacement ?
I have two class MainPage.xaml and Home.xaml. I have written Home.xaml using Xamarin.Essentials shake detect code and want to move from Home to MainPage.xaml but it is not working.
Home.xaml.cs
namespace StarRatingApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Home : ContentPage
{
public Home()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
Accelerometer.ShakeDetected += Accelerometer_ShakeDetected;
Accelerometer.Start(SensorSpeed.Game);
}
private void Accelerometer_ShakeDetected(object sender, EventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
new NavigationPage(new MainPage());
});
}
}
What am I doing wrong in Accelerometer_ShakeDetected method ??
You are not navigating in the method. You just create a new NavigationPage. And this won't do anything. You have to tell the system, where you want to navigate and how.
You have two options:
1) Navigate to your page (this adds the page to the Navigation-Stack)
await Navigation.PushAsync(new MainPage());
or 2) Overwrite your mainpage with the new NavigationPage (this removes the whole NavigationStack and starts a "new" navigation)
Application.Current.MainPage = new NavigationPage(new MainPage());
You can find more infos about navigation in the documentation >> https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical
I would like to create an application with a navigation drawer. So each page should be accessible via drawer "link" item.
To protect those pages the application should only be accessible on a valid session otherwise the login page should be rendered.
I'm trying to explain what I've done so far for reproduction purposes...
After creating a new Xamarin forms project I created a MainPage holding the drawer and the currently rendered page
public class MainPage : MasterDetailPage
{
public MainPage()
{
Master = new MasterPage();
Detail = new NavigationPage(new DummyPage()); // Set initial page
}
}
So the MasterPage (drawer) is empty for now
internal class MasterPage : ContentPage
{
public MasterPage()
{
Title = "Master";
Content = new StackLayout { Children = { } };
}
}
Same for the rendered DummyPage
class DummyPage : ContentPage
{
public DummyPage()
{
Title = "Dummy";
Content = new StackLayout { Children = { new Label { Text = "Dummy Page" } } };
}
}
So without a LoginPage my App file would look like
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
}
The rendered application then looks fine
When I try to render a LoginPage before accessing the main application part I would do something like
public partial class App : Application
{
public App()
{
InitializeComponent();
Page initialPage;
if (false) // check if logged in
{
initialPage = new LoginPage();
}
else
{
initialPage = new MainPage();
}
MainPage = new NavigationPage(initialPage);
}
}
Unfortunately the rendered MainPage then looks like
How can I fix that? What is missing or wrong?
Update
I found out that I can set the current displayed page globally. So for the start I do
public App()
{
InitializeComponent();
Page initialPage;
if (isLoggedIn)
{
initialPage = new LoginPage();
}
else
{
initialPage = new MainPage();
}
MainPage = initialPage;
}
and when clicking the "Sign In" button on the LoginPage I don't execute
SignInCommand = new Command(() =>
{
await Application.Current.MainPage.Navigation.PushAsync(new MainPage());
});
This works for me
SignInCommand = new Command(() =>
{
Application.Current.MainPage = new MainPage();
});
You should try this
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage(false);
}
}
public class MainPage : MasterDetailPage
{
public MainPage(bool value)
{
Master = new MasterPage();
Detail = value ? new NavigationPage(new LoginPage()) : new NavigationPage(new DummyPage());
}
}
I think navigation doesn't add the main container.
It seems that you put the MasterDetailPage in a NavigationPage . Actually , it is nappropriate to do it . You could improve the code like following
public partial class App : Application
{
public App()
{
InitializeComponent();
if (false) // check if logged in
{
MainPage = new NavigationPage(initialPage);
}
else
{
MainPage = new MainPage();
}
}
}
I'm doing a Master Detail Page as a menu and I'm getting this error:
PushAsync is not supported globally on Android, please use a NavigationPage.
When I try to instantiate another page. I believe my code already creates a navigation page when instantiate the page, but I'm not sure. Can someone help me?
App.xaml.cs:
public partial class App : Application
{
static public MasterDetailPage MasterDetail { get; set; }
public async static Task NavigateMasterDetail(Page page)
{
App.MasterDetail.IsPresented = false;
await App.MasterDetail.Navigation.PushAsync(page);
}
public App ()
{
InitializeComponent();
MainPage = new selectPage();
}
Page that carry the menu, selectPage:
public partial class selectPage : MasterDetailPage
{
public selectPage()
{
InitializeComponent();
this.Master = new Master();
this.Detail = new NavigationPage(new Detail());
App.MasterDetail = this;
}
}
Master.xaml.cs:
public Master ()
{
InitializeComponent ();
toDivPage.Clicked += async (sender, e) =>
{
await App.NavigateMasterDetail(new MainPage());
};
toBiqPage.Clicked += async (sender, e) =>
{
await App.NavigateMasterDetail(new MainPage());
};
}
Detail.xaml.cs is empty.
I believe you have to use NavigationPage in your App.xaml.cs.
In your App.xaml.cs make sure your constructor or App method looks like this
public App ()
{
InitializeComponent();
MainPage = new NavigationPage(new selectPage());
}
I hope this helps
I am trying to keep the Master page (menu) to the left of My MasterDetailPage at all times, I do not want the navigation to take the user away from the MasterDetailPage and make the user press a back button to get back.
I am looking a traditional burger bar menu to the left that simply changes the Detail page and keeps the master in place.
What I have now:
public partial class App : Application
{
public static NavigationPage NavPage { get; private set; }
private static RootPage RootPage;
public static bool MenuIsPresented
{
get
{
return RootPage.IsPresented;
}
set
{
RootPage.IsPresented = value;
}
}
public App()
{
InitializeComponent();
MenuPage menuPage = new MenuPage();
NavPage = new NavigationPage(new FirstPage());
RootPage = new RootPage();
RootPage.Master = menuPage;
RootPage.Detail = NavPage;
MainPage = RootPage;
}
...
}
And my ViewModel Command
async void GoSecondPageAsync(object obj)
{
await App.NavPage.PushAsync(new SecondPage());
App.MenuIsPresented = false;
}
But this just creates a new page on top of the stack with a back button back to the MasterDetailPage, while all I want is to stay within the MasterDetailPage.
You can push the new page and just remove the first one from the NavigationStack For example:
async void GoSecondPageAsync(object obj)
{
await App.NavPage.PushAsync(new SecondPage());
var removePage = App.NavPage.Navigation.NavigationStack[0];
App.NavPage.Navigation.RemovePage(removePage);
App.MenuIsPresented = false;
}
Although, if you just always want to replace the page do you really even need the NavigationPage and can't you just set the RootPage.Detail=new SecondPage();
You just need to update the Detail property to point to the new page. Like this:
async void GoSecondPageAsync(object obj)
{
RootPage.Detail = new NavigationPage(new SecondPage());
App.MenuIsPresented = false;
}