Is there a way to tell if a ContentPage is currently shown?
I need code inside an event handler in the ContentPage to check whether the page is currently shown and act accordingly.
In addition to GBreen12's answer, you could also do it this way...
bool isShowingMyPage = Application.Current.MainPage is MyPage || (Application.Current.MainPage is NavigationPage navPage && navPage.CurrentPage is MyPage); //If you are using a MasterDetailPage or something else, then you would have to handle this differently
//I think the below will work for checking if any modals are showing over your page but you should definitely test it in different scenarios to make sure. I am not sure if the modal is immediately removed from ModalStack when it gets popped or not
isShowingMyPage = Application.Current.MainPage?.Navigation?.ModalStack?.LastOrDefault() == null;
You can override OnAppearing which is called anytime the page is about to be shown:
Page lifecycle events in xamarin.forms
You can listen to the NavigationPage's Pushed and Popped events, like so:
((Xamarin.Forms.NavigationPage)MyProject.App.Current.MainPage).Pushed += (s, e) =>
{
if (e.Page is ContentPage)
{
// Do what you gotta do
}
// or, for a specific page:
if (e.Page is MyProject.Views.MyCustomPage)
{
// Do what you gotta do
}
};
Of course, this will only be called when the page is pushed onto the navigation stack; If you need it to be called each time the page appears on the screen, then go with what GBreen12 or hvaughan3 said.
Related
I have 3 BladeItems in another page. And I want to navigate from MainPage to that page and bring the requested BladeItem into view. But it is not working.
I first thought it was because that the page has not been loaded. So I put it into the Page_Loaded. However, it is still now working. Why is that?
private void Page_Loaded(object sender, RoutedEventArgs e)
{
TitleBarHelper.SetDarkTitleBar();
Window.Current.SetTitleBar(AppTitleBar);
UpdateTitleBarLayout(Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().TitleBar);
FullMediaControl.Update();
SetMusic(MediaHelper.CurrentMusic);
FullPlaylistControl.ScrollToMusic(MediaHelper.CurrentMusic);
if (MusicInfoRequestedWhenUnloaded)
{
MusicPropertyBladeItem.StartBringIntoView();
MusicInfoRequestedWhenUnloaded = false;
}
else if (LyricsRequestedWhenUnloaded)
{
LyricsBladeItem.StartBringIntoView();
LyricsRequestedWhenUnloaded = false;
}
}
Source Page Code is here. This page can be navigated using the "Show Lyrics" or "Music Info" item in the MenuFlyout at the right bottom more button.
And actually the FullPlaylistControl.ScrollToMusic in the code above is also not working. It just scrolls to a row in a ListView. I guess they might be the same reason.
This is the documentation for StartBringIntoView.
According to the instructions in the documentation, this method is only possible when the control is rendered on the visual tree, so you need to modify it when you call the method.
You want MusicPropertyBladeItem.StartBringIntoView() to work, you need to call it in the MusicPropertyBladeItem_Loaded event. For the same reason, you need to call ScrollToMusic when the FullPlaylistControl is loaded.
Page_Loaded only means that the page is loaded, but it doesn't mean that the controls have been rendered.
Best ragards.
I have tried to different methods to get my MainPage to change and it is not happening. Basically when my App starts it needs to do some Async tasks to initialize everything, and then get the real main page to display. I first tried an event handler in App.cs. I can confirm the event handler does fire off in my debugger. However the code to switch the main page is not successfully doing so. Nothing changes in my UI. This code is below:
private void UnitStatusModelChanged(object sender, EventArgs e)
{
UnitStatusModel = _unitStatusMonitor.UnitStatusModel;
UnitStatusViewModel = new UnitStatusViewModel(UnitStatusModel, InputResourceHandler);
MainPage = new MainTabbed(UnitStatusViewModel, InputResourceHandler);
Debug.WriteLine("Switched page to " + UnitStatusModel.Version.Name);
}
protected override void OnStart()
{
_unitStatusMonitor.UnitStatusChanged += new UnitStatusModelChangedEventHandler(UnitStatusModelChanged);
_commandQueue.StartQueue();
}
I thought maybe setting MainPage this way is not the way to go. So I tried making the MainPage a blank NavigationPage, and then pushing a model page on top once the app is ready. This also had no effect.
var newPage = new MainTabbed(
new UnitStatusViewModel(_unitStatusModel, inputResourceHandler),
inputResourceHandler
);
App.Current.MainPage.Navigation.PushModalAsync(newPage);
I can confirm that if I start the app with a MainTabbed page, the page does display, so it should not be a problem with MainTabbed. Again, I also verified the in the debugger these lines of code are being executed with no exceptions being thrown. Still, no change to MainPage. What gives?
Check your thread after your async completes, when you call PushModalAsync (or any UI thing). Make sure it's on the main thread.
When my app starts, I so some logic in my AppDelegate and assign the MainPage a page based on the results of that logic.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init ();
// .....
if(authorizationStatus == PhotoLibraryAuthorizationStatus.Authorized)
{
bokehApp.SetStartupView(serviceLocator.GetService<AlbumsPage>());
}
else
{
bokehApp.SetStartupView(serviceLocator.GetService<StartupPage>());
}
}
In my app.cs, I assign the MainPage the given view from the AppDelegate
public class App : Xamarin.Forms.Application
{
public void SetStartupView(ContentPage page)
{
this.MainPage = new NavigationPage(page);
}
}
In this instance, I am passing StartupPage into the SetStartupView(Page) method. When the user does something, I navigate to the AlbumsPage.
this.Navigation.PushAsync(new AlbumPage());
When I do this, the AlbumPage is created and navigated to; it's Navigation.NavigationStack collection only contains itself, not the Page that it just navigated from. What I want to do, is prevent a call to this.Navigation.PopAsync() navigating back to the StartupPage, which is what currently happens.
Initially I was going to just run a loop and pop the original Page off, and then remove all of the remaining pages, in this case StartupPage.
// Grab the current page, as it is about to become our new Root.
var navigatedPages = this.Navigation.NavigationStack.ToList();
Page newRootPage = navigatedPages.Last();
// Dont include the current item on the stack in the removal.
navigatedPages.Remove(newRootPage);
while(navigatedPages.Count > 0)
{
Page currentPage = navigatedPages.Last();
this.Navigation.RemovePage(currentPage);
}
However, when I look, the Navigation.NavigationStack collection only contains the AlbumsPage. Yet, calling this.Navigation.PopAsync() navigates back to the StartupPage.
What do I need to do in order to reset this navigation stack, so that popping will not navigate back to the initial page?
Update
When I navigate, I've been able to use this:
App.Current.MainPage = new NavigationPage(viewModelPage);
as suggested by #Daniel but this prevents the animation from taking place. I have also tried
await App.Current.MainPage.Navigation.PushAsync(fooPage);
App.Current.MainPage = new NavigationPage(fooPage);
When I do this, I see the new page transitioned to, but once the await call on PushAsync finishes, and the MainPage is replaced, the Page disappears and I'm left with an empty screen.
I really don't want to lose the animations during transitioning from my setup Page to the actual app.
I think what you're looking for is setting:
App.Current.MainPage
to a new NavigationPage. It replaces main page of application.
I was able to solve the issue. This mostly came down to my lack of understanding how the NavigationPage worked. It would seem that each Page is given their own Navigation stack. As I navigated between Pages and examined their NavigationStack collections, they always just had one item in them.
I then started looking at App.Current.MainPage.Navigation and discovered that it actually had the entire stack (both StartupPage and FooPage). I was able to then grab the StartupPage prior to pushing the FooPage onto the navigation stack, and then removing the StartupPage once the navigation to FooPage was completed. This essentially let me reset the root page, while keeping the transition animation between the views.
Page originalRootPage = App.Current.MainPage.Navigation.NavigationStack.Last();
await App.Current.MainPage.Navigation.PushAsync(new FooPage());
App.Current.MainPage.Navigation.RemovePage(originalRootPage);
I'll mark this as answered when the time-period has expired and i'm allowed to.
This is fix my problem.
protected override bool OnBackButtonPressed()
{
foreach (Page page in Navigation.ModalStack)
{
page.Navigation.PopModalAsync();
}
return true;
}
I pass a PhoneApplicationPage instance to a classlibrary, and popup an usercontrol in this classlibrary, when I press back button, the whole application exit. Yesterday I sovled the problem in an application, but I cannot use the method in this classlibrary case.
I tried to subscribe to the event(BackKeyPress), but VS2012 says "parent_BackKeyPress" "System.EventHandler" override and delegate cannot match. I checked, they match.
PhoneApplicationPage mContext=...;
mContext.BackKeyPress += new EventHandler(parent_BackKeyPress);
void parent_BackKeyPress(CancelEventArgs e)
{
ppChangePIN.IsOpen = false;
Application.Current.RootVisual.Visibility = Visibility.Visible;
}
anything incorrect here? plus, can I use navigationservice in classlibrary? I did this before to navigate to a page created in the classlibrary like below, well it ends up crashing. Some say can't use pages in classlibrary, instead we should use Popup(usercontrol).
mContext.NavigationService.Navigate(new Uri("/ChangePINPage.xaml", UriKind.Relative));
I have successfully done just that:
// or some other method of accessing the current page
// - but via Application, to which you have access also in class library
var currentPage = (PhoneApplicationPage)((PhoneApplicationFrame)Application.Current.RootVisual).Content;
currentPage.BackKeyPress += (sender, args) =>
{
// Display dialog or something, and when you decide not to perform back navigation:
args.Cancel = true;
};
Of course you have to make sure that this code is executed if and only if the CurrentPage is the main page.
I also use Pages in class library. You can use NavigationService in class library: you can get it for example from current page obtained as above (currentPage.NavigationService). Or you could use the Navigate method of PhoneApplicationFrame:
((PhoneApplicationFrame)Application.Current.RootVisual)
.Navigate(
new Uri(
"/ClassLibraryName;component/SamplePage.xaml",
UriKind.Relative));
As the short Uris like "/SamplePage.xaml" will work in Application Project, to navigate to page in class library you have to give full location: "/ClassLibraryName;component/SamplePage.xaml".
But note, that if the application chooses to display message box to stop from exiting, it will not pass certification, as (from Technical certification requirements for Windows Phone):
5.2.4.2 – Back button: first screen
Pressing the Back button from the first screen of an app must close the app.
I am currently working on an app for WP7 for my university, and need a temporary solution to a problem. Now this solution is, that I will be loading a webpage using the web browser control for WP7. For example: http://m.iastate.edu/laundry/
Now as you see on the webpage, there are certain elements I want to hide, for example the back button. For now, what I have done to handle the back button is something like this:
private void webBrowser1_Navigating(object sender, NavigatingEventArgs e)
{
// Handle loading animations
// Handle what happens when the "back" button is pressed
Uri home = new Uri("http://m.iastate.edu/");
// The the current loading address is home
// Cancel the navigation, and go back to the
// apps home page.
if (e.Uri.Equals(home))
{
e.Cancel = true;
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
Now that works beautifully, except for the part that there is a back button on the hardware.
So my second option is to completely hide the back button ONLY on that page, and not its children. So not on http://m.iastate.edu/laundry/l/0
I am still debating on just parsing the data and displaying it in my own style, but I'm not sure if that's completely needed seeing how the data needs constant internet service and is already in a well-put format. Plus, I feel like that would be a waste of resources? Throw in your opinions on that too :)
Thanks!
You should inject a script in the page with InvokeScript.
Here is the kind of Javascript code you need to remove the back button:
// get the first child element of the header
var backButton = document.getElementsByTagName("header")[0].firstChild;
// check if it looks like a back button
if(backButton && backButton.innerText == "Back") {
// it looks like a back button, remove it
document.getElementsByTagName("header")[0].removeChild[backButton];
}
Call this script with InvokeScript:
webBrowser1.InvokeScript("eval", "(function() { "+ script +"}()");
Warning: IsScriptEnabled must be set to true on the web control
If the removal of the back button depends of the page, just test the navigating URI in C# and inject the script if neeeded.